- Your Badge
-
- Static
-
-
-
-
- Using dash "-" separator
-
- --`} />
-
-
- Using query string parameters
-
- &message=&color=`}
- />
-
-
- Colors
-
-
-
-
-
-
-
-
- Endpoint
-
-
- &style
\ No newline at end of file
diff --git a/logo/stackexchange.svg b/logo/stackexchange.svg
deleted file mode 100644
index a5f409d70650e..0000000000000
--- a/logo/stackexchange.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/logo/superuser.svg b/logo/superuser.svg
deleted file mode 100644
index 443f7682d6a31..0000000000000
--- a/logo/superuser.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/logo/telegram.svg b/logo/telegram.svg
deleted file mode 100644
index c92624f6b662d..0000000000000
--- a/logo/telegram.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/logo/travis.svg b/logo/travis.svg
deleted file mode 100644
index bd858287c4768..0000000000000
--- a/logo/travis.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/migrations/1676731511125_initialize.cjs b/migrations/1676731511125_initialize.cjs
new file mode 100644
index 0000000000000..3bdf04ca92358
--- /dev/null
+++ b/migrations/1676731511125_initialize.cjs
@@ -0,0 +1,12 @@
+exports.shorthands = undefined
+
+exports.up = pgm => {
+ pgm.createTable('github_user_tokens', {
+ id: 'id',
+ token: { type: 'varchar(1000)', notNull: true, unique: true },
+ })
+}
+
+exports.down = pgm => {
+ pgm.dropTable('github_user_tokens')
+}
diff --git a/migrations/1727809177709_add-created.cjs b/migrations/1727809177709_add-created.cjs
new file mode 100644
index 0000000000000..ceafc9a8976ed
--- /dev/null
+++ b/migrations/1727809177709_add-created.cjs
@@ -0,0 +1,15 @@
+exports.shorthands = undefined
+
+exports.up = pgm => {
+ pgm.addColumn('github_user_tokens', {
+ created: {
+ type: 'TIMESTAMP',
+ notNull: true,
+ default: pgm.func('CURRENT_TIMESTAMP'),
+ },
+ })
+}
+
+exports.down = pgm => {
+ pgm.dropColumn('github_user_tokens', 'created')
+}
diff --git a/migrations/1780134030603_add-token-scopes.cjs b/migrations/1780134030603_add-token-scopes.cjs
new file mode 100644
index 0000000000000..9a820b2133850
--- /dev/null
+++ b/migrations/1780134030603_add-token-scopes.cjs
@@ -0,0 +1,11 @@
+exports.shorthands = undefined
+
+exports.up = pgm => {
+ pgm.addColumn('github_user_tokens', {
+ scopes: { type: 'text[]' },
+ })
+}
+
+exports.down = pgm => {
+ pgm.dropColumn('github_user_tokens', 'scopes')
+}
diff --git a/now.json b/now.json
deleted file mode 100644
index 517a49a9a6027..0000000000000
--- a/now.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "version": 1,
- "name": "shields",
- "env": {
- "PERSISTENCE_DIR": "/tmp/persistence"
- },
- "type": "npm",
- "engines": {
- "node": "8.x"
- }
-}
diff --git a/package-lock.json b/package-lock.json
index c129a70e8c2f4..23b5b939af04f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,31126 +1,34291 @@
{
"name": "shields.io",
"version": "0.0.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
- "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.0.0"
+ "packages": {
+ "": {
+ "name": "shields.io",
+ "version": "0.0.0",
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "@renovatebot/pep440": "^5.0.0",
+ "@renovatebot/ruby-semver": "^5.0.0",
+ "@sentry/node-core": "^10.67.0",
+ "@shields_io/camp": "^18.1.2",
+ "@xmldom/xmldom": "0.9.10",
+ "badge-maker": "file:badge-maker",
+ "byte-size": "^9.0.1",
+ "camelcase": "^9.0.0",
+ "chalk": "^5.6.2",
+ "check-node-version": "^4.2.1",
+ "cloudflare-middleware": "^1.0.4",
+ "config": "^4.4.2",
+ "cross-env": "^10.1.0",
+ "dayjs": "^1.11.21",
+ "decamelize": "^6.0.1",
+ "emojic": "^1.1.18",
+ "escape-string-regexp": "^5.0.0",
+ "fast-xml-parser": "^5.10.1",
+ "glob": "^13.0.6",
+ "global-agent": "^4.1.3",
+ "got": "^15.1.0",
+ "graphql": "17.0.2",
+ "graphql-tag": "^2.12.7",
+ "joi": "18.2.3",
+ "joi-extension-semver": "5.0.0",
+ "js-yaml": "^5.2.1",
+ "jsonpath-plus": "^10.4.0",
+ "lodash.countby": "^4.6.0",
+ "matcher": "^6.0.0",
+ "mvncmp": "^1.0.0",
+ "node-pg-migrate": "^9.0.0",
+ "parse-link-header": "^2.0.0",
+ "path-to-regexp": "^6.3.0",
+ "pg": "^8.22.0",
+ "priorityqueuejs": "^2.0.0",
+ "prom-client": "^15.1.3",
+ "qs": "^6.15.3",
+ "re2": "^1.26.1",
+ "semver": "~7.8.5",
+ "simple-icons": "16.26.0",
+ "smol-toml": "1.7.0",
+ "svg-path-bbox": "^2.1.0",
+ "svgpath": "^2.6.0",
+ "webextension-store-meta": "^1.3.0",
+ "xpath": "~0.0.34"
+ },
+ "devDependencies": {
+ "@docusaurus/core": "^3.10.2",
+ "@docusaurus/preset-classic": "^3.10.2",
+ "@easyops-cn/docusaurus-search-local": "^0.55.2",
+ "@mdx-js/react": "^3.1.1",
+ "@typescript-eslint/parser": "^8.65.0",
+ "c8": "^12.0.0",
+ "caller": "^1.1.0",
+ "chai": "6.2.2",
+ "chai-as-promised": "^8.0.2",
+ "chai-datetime": "^1.8.1",
+ "clsx": "^2.1.1",
+ "concurrently": "^10.0.3",
+ "cypress": "^15.19.0",
+ "cypress-wait-for-stable-dom": "^0.1.0",
+ "danger": "^13.0.10",
+ "deepmerge": "^4.3.1",
+ "docusaurus-preset-openapi": "0.8.0",
+ "eslint": "9.39.2",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-chai-friendly": "1.2.1",
+ "eslint-plugin-cypress": "6.4.3",
+ "eslint-plugin-icedfrisby": "0.3.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-jsdoc": "63.2.0",
+ "eslint-plugin-mocha": "11.3.0",
+ "eslint-plugin-prettier": "5.5.6",
+ "eslint-plugin-promise": "7.3.0",
+ "eslint-plugin-react-hooks": "7.1.1",
+ "eslint-plugin-sort-class-members": "1.22.1",
+ "globals": "17.7.0",
+ "http-deceiver": "file:vendor/http-deceiver",
+ "icedfrisby": "4.0.0",
+ "icedfrisby-nock": "^2.1.0",
+ "is-svg": "^6.1.0",
+ "jsdoc": "^4.0.5",
+ "lint-staged": "^17.1.1",
+ "mocha": "^11.7.6",
+ "neostandard": "0.13.0",
+ "nock": "13.5.6",
+ "node-mocks-http": "^1.18.1",
+ "nodemon": "^3.1.14",
+ "npm-run-all": "^4.1.5",
+ "open-cli": "^9.0.0",
+ "portfinder": "^1.0.38",
+ "prettier": "3.9.6",
+ "prism-react-renderer": "^2.4.1",
+ "react": "^19.2.8",
+ "react-dom": "^19.2.8",
+ "rimraf": "^6.1.3",
+ "sazerac": "^2.0.0",
+ "simple-git-hooks": "^2.13.1",
+ "sinon": "^22.1.0",
+ "sinon-chai": "4.0.1",
+ "snap-shot-it": "^7.9.10",
+ "start-server-and-test": "3.0.11",
+ "tsd": "^0.33.0",
+ "url": "^0.11.4"
+ },
+ "engines": {
+ "node": "^24 || ^26",
+ "npm": "^11 || ^12"
}
},
- "@babel/compat-data": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.5.tgz",
- "integrity": "sha512-jWYUqQX/ObOhG1UiEkbH5SANsE/8oKXiQWjj7p7xgj9Zmnt//aUvyz4dBkK0HNsS8/cbyC5NmmH87VekW+mXFg==",
- "dev": true,
- "requires": {
- "browserslist": "^4.8.5",
- "invariant": "^2.2.4",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "browserslist": {
- "version": "4.8.6",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz",
- "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001023",
- "electron-to-chromium": "^1.3.341",
- "node-releases": "^1.1.47"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001023",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
- "integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==",
- "dev": true
- },
- "electron-to-chromium": {
- "version": "1.3.344",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
- "integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==",
- "dev": true
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
- },
- "@babel/core": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
- "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.0",
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helpers": "^7.9.0",
- "@babel/parser": "^7.9.0",
- "@babel/template": "^7.8.6",
- "@babel/traverse": "^7.9.0",
- "@babel/types": "^7.9.0",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.1",
- "json5": "^2.1.2",
- "lodash": "^4.17.13",
- "resolve": "^1.3.2",
- "semver": "^5.4.1",
- "source-map": "^0.5.0"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/generator": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz",
- "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.9.0",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
- "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-simple-access": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/template": "^7.8.6",
- "@babel/types": "^7.9.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/highlight": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
- "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
- "@babel/template": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
- "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.6",
- "@babel/types": "^7.8.6"
- }
- },
- "@babel/traverse": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz",
- "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.0",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.9.0",
- "@babel/types": "^7.9.0",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "json5": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
- "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "badge-maker": {
+ "version": "6.0.0",
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "anafanafo": "2.0.0",
+ "css-color-converter": "^2.0.0"
+ },
+ "bin": {
+ "badge": "lib/badge-cli.js"
+ },
+ "engines": {
+ "node": ">=22"
}
},
- "@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "node_modules/@11ty/gray-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@11ty/gray-matter/-/gray-matter-1.0.0.tgz",
+ "integrity": "sha512-7mJJl+wf1AByoT0PknQiQfOPnVNT4fevGrUBVWO4HXsnYn1aQPyRyrELYrNUFleUBM++KzMKN6QaxHPk0t/6/g==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "js-yaml": "^4.1.0",
+ "kind-of": "^6.0.3",
+ "section-matter": "^1.0.0",
+ "strip-bom-string": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=11"
}
},
- "@babel/helper-annotate-as-pure": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz",
- "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==",
+ "node_modules/@11ty/gray-matter/node_modules/js-yaml": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
- "dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
}
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz",
- "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==",
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
"dev": true,
- "requires": {
- "@babel/helper-explode-assignable-expression": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
- "dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "@babel/helper-builder-react-jsx": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz",
- "integrity": "sha512-kvbfHJNN9dg4rkEM4xn1s8d1/h6TYNvajy9L1wx4qLn9HFg0IkTsQi4rfBe92nxrPUFcMsHoMV+8rU7MJb3fCA==",
+ "node_modules/@algolia/abtesting": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.22.0.tgz",
+ "integrity": "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g==",
"dev": true,
- "requires": {
- "@babel/types": "^7.7.4",
- "esutils": "^2.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
- "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-builder-react-jsx-experimental": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz",
- "integrity": "sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ==",
+ "node_modules/@algolia/autocomplete-core": {
+ "version": "1.19.9",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.9.tgz",
+ "integrity": "sha512-4U2JKLMWlDu0CotYyUkWakDxr8AIav3QtIUXXRpfavYN29aVWfzlwJp9T0rPKEf/dO2QCPAUc0Kq1Tj1GJxo2A==",
"dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/types": "^7.9.0"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/autocomplete-plugin-algolia-insights": "1.19.9",
+ "@algolia/autocomplete-shared": "1.19.9"
}
},
- "@babel/helper-call-delegate": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz",
- "integrity": "sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A==",
+ "node_modules/@algolia/autocomplete-plugin-algolia-insights": {
+ "version": "1.19.9",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.9.tgz",
+ "integrity": "sha512-6mExC6X7762s2SV3eJy3QOkB8bdMmnUhQ2agvGVDuzwoGyr3PquGSY/0vPQXCfiAiCaXUz1rXn+lwghgSi0l0w==",
"dev": true,
- "requires": {
- "@babel/helper-hoist-variables": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/autocomplete-shared": "1.19.9"
+ },
+ "peerDependencies": {
+ "search-insights": ">= 1 < 3"
}
},
- "@babel/helper-compilation-targets": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz",
- "integrity": "sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg==",
+ "node_modules/@algolia/autocomplete-shared": {
+ "version": "1.19.9",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.9.tgz",
+ "integrity": "sha512-YosP9Uoek6y/Ur1r1qeogk4biMe/hzkyNcgMCciw0//3XpCM7VlYLSHnyt/vOnEOGhCCc0+3v+unEiH6zz+Z1A==",
"dev": true,
- "requires": {
- "@babel/compat-data": "^7.8.4",
- "browserslist": "^4.8.5",
- "invariant": "^2.2.4",
- "levenary": "^1.1.1",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "browserslist": {
- "version": "4.8.6",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz",
- "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001023",
- "electron-to-chromium": "^1.3.341",
- "node-releases": "^1.1.47"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001023",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
- "integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==",
- "dev": true
- },
- "electron-to-chromium": {
- "version": "1.3.344",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
- "integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==",
- "dev": true
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "license": "MIT",
+ "peerDependencies": {
+ "@algolia/client-search": ">= 4.9.1 < 6",
+ "algoliasearch": ">= 4.9.1 < 6"
}
},
- "@babel/helper-create-class-features-plugin": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz",
- "integrity": "sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg==",
+ "node_modules/@algolia/client-abtesting": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.56.0.tgz",
+ "integrity": "sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==",
"dev": true,
- "requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-split-export-declaration": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-create-regexp-features-plugin": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz",
- "integrity": "sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q==",
+ "node_modules/@algolia/client-analytics": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.56.0.tgz",
+ "integrity": "sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==",
"dev": true,
- "requires": {
- "@babel/helper-regex": "^7.8.3",
- "regexpu-core": "^4.6.0"
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-define-map": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz",
- "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==",
+ "node_modules/@algolia/client-common": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.56.0.tgz",
+ "integrity": "sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==",
"dev": true,
- "requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/types": "^7.8.3",
- "lodash": "^4.17.13"
- },
- "dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-explode-assignable-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz",
- "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==",
+ "node_modules/@algolia/client-insights": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.56.0.tgz",
+ "integrity": "sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==",
"dev": true,
- "requires": {
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-function-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz",
- "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==",
+ "node_modules/@algolia/client-personalization": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.56.0.tgz",
+ "integrity": "sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==",
"dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-get-function-arity": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
- "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+ "node_modules/@algolia/client-query-suggestions": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.56.0.tgz",
+ "integrity": "sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-hoist-variables": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz",
- "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==",
+ "node_modules/@algolia/client-search": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.56.0.tgz",
+ "integrity": "sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-member-expression-to-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
- "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
+ "node_modules/@algolia/events": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz",
+ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT"
+ },
+ "node_modules/@algolia/ingestion": {
+ "version": "1.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.56.0.tgz",
+ "integrity": "sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz",
- "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-module-imports": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
- "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "node_modules/@algolia/monitoring": {
+ "version": "1.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.56.0.tgz",
+ "integrity": "sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==",
"dev": true,
- "requires": {
- "@babel/types": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-module-transforms": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz",
- "integrity": "sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-simple-access": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3",
- "lodash": "^4.17.13"
- },
- "dependencies": {
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "node_modules/@algolia/recommend": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.56.0.tgz",
+ "integrity": "sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-optimise-call-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
- "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
+ "node_modules/@algolia/requester-browser-xhr": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.56.0.tgz",
+ "integrity": "sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz",
- "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@algolia/client-common": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-plugin-utils": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
- "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
- "dev": true
- },
- "@babel/helper-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz",
- "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==",
+ "node_modules/@algolia/requester-fetch": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.56.0.tgz",
+ "integrity": "sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==",
"dev": true,
- "requires": {
- "lodash": "^4.17.13"
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-remap-async-to-generator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz",
- "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==",
- "dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-wrap-function": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
- "dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
- }
- },
- "@babel/helper-replace-supers": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
- "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
- "dev": true,
- "requires": {
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.6",
- "@babel/types": "^7.8.6"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/generator": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz",
- "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.7",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz",
- "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==",
- "dev": true
- },
- "@babel/traverse": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
- "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.6",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.6",
- "@babel/types": "^7.8.6",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/types": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz",
- "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "node_modules/@algolia/requester-node-http": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.56.0.tgz",
+ "integrity": "sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
}
},
- "@babel/helper-simple-access": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz",
- "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==",
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
"dev": true,
- "requires": {
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "@babel/helper-split-export-declaration": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
- "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/helper-validator-identifier": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz",
- "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==",
- "dev": true
- },
- "@babel/helper-wrap-function": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz",
- "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==",
- "dev": true,
- "requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
- "dependencies": {
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
- }
- },
- "@babel/helpers": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz",
- "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.9.0",
- "@babel/types": "^7.9.0"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/generator": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz",
- "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.9.0",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "@babel/highlight": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
- "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
- "@babel/traverse": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz",
- "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.0",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.9.0",
- "@babel/types": "^7.9.0",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
+ "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/highlight": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
- "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "node_modules/@babel/core": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz",
+ "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
"dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- },
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- }
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.26.0",
+ "@babel/generator": "^7.26.0",
+ "@babel/helper-compilation-targets": "^7.25.9",
+ "@babel/helper-module-transforms": "^7.26.0",
+ "@babel/helpers": "^7.26.0",
+ "@babel/parser": "^7.26.0",
+ "@babel/template": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.26.0",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
}
},
- "@babel/parser": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.0.tgz",
- "integrity": "sha512-SmjnXCuPAlai75AFtzv+KCBcJ3sDDWbIn+WytKw1k+wAtEy6phqI2RqKh/zAnw53i1NR8su3Ep/UoqaKcimuLg==",
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
},
- "@babel/plugin-proposal-async-generator-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz",
- "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==",
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-remap-async-to-generator": "^7.8.3",
- "@babel/plugin-syntax-async-generators": "^7.8.0"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "@babel/plugin-proposal-class-properties": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz",
- "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==",
- "dev": true,
- "requires": {
- "@babel/helper-create-class-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/generator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz",
- "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "@babel/helper-create-class-features-plugin": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.3.tgz",
- "integrity": "sha512-qmp4pD7zeTxsv0JNecSBsEmG1ei2MqwJq4YQcK3ZWm/0t07QstWfvuV/vm3Qt5xNMFETn2SZqpMx2MQzbtq+KA==",
- "dev": true,
- "requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3"
- }
- },
- "@babel/helper-function-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz",
- "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==",
- "dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-get-function-arity": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
- "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-member-expression-to-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
- "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-optimise-call-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
- "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/helper-replace-supers": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz",
- "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==",
- "dev": true,
- "requires": {
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
- "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz",
- "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==",
- "dev": true
- },
- "@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/traverse": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz",
- "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.3",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-proposal-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==",
+ "node_modules/@babel/generator/node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0"
+ "bin": {
+ "jsesc": "bin/jsesc"
},
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- }
+ "engines": {
+ "node": ">=6"
}
},
- "@babel/plugin-proposal-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz",
- "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==",
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
+ "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.0"
+ "dependencies": {
+ "@babel/types": "^7.25.9"
},
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.7.4.tgz",
- "integrity": "sha512-TbYHmr1Gl1UC7Vo2HVuj/Naci5BEGNZ0AJhzqD2Vpr6QPFWpUmBRLrIDjedzx7/CShq0bRDS2gI4FIs77VHLVQ==",
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.7.4"
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "@babel/plugin-proposal-numeric-separator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz",
- "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==",
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
+ "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/helper-replace-supers": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-proposal-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==",
+ "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "@babel/plugin-proposal-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==",
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
+ "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "regexpu-core": "^6.2.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-proposal-optional-chaining": {
- "version": "7.7.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.7.5.tgz",
- "integrity": "sha512-sOwFqT8JSchtJeDD+CjmWCaiFoLxY4Ps7NjvwHC/U7l4e9i5pTRNt8nDMIFSOUL+ncFbYSwruHM8WknYItWdXw==",
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-syntax-optional-chaining": "^7.7.4"
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "@babel/plugin-proposal-unicode-property-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz",
- "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==",
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz",
+ "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==",
"dev": true,
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "debug": "^4.4.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.10"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "@babel/plugin-syntax-dynamic-import": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz",
- "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==",
+ "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
+ "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-jsx": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz",
- "integrity": "sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==",
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.7.4.tgz",
- "integrity": "sha512-XKh/yIRPiQTOeBg0QJjEus5qiSKucKAiApNtO1psqG7D17xmE+X2i5ZqBEuSvo0HRuyPaKaSN/Gy+Ha9KFQolw==",
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-syntax-numeric-separator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz",
- "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==",
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
+ "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz",
+ "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-wrap-function": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.7.4.tgz",
- "integrity": "sha512-2MqYD5WjZSbJdUagnJvIdSfkb/ucOC9/1fRJxm7GAxY6YQLWlUvkfxoNbUPcPLHJyetKUDQ4+yyuUyAoc0HriA==",
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
+ "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.25.9",
+ "@babel/helper-optimise-call-expression": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-syntax-top-level-await": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz",
- "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==",
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
+ "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-syntax-typescript": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz",
- "integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==",
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-transform-arrow-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
- "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==",
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-transform-async-to-generator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz",
- "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-remap-async-to-generator": "^7.8.3"
- },
- "dependencies": {
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-transform-block-scoped-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz",
- "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==",
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz",
+ "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/template": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-transform-block-scoping": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz",
- "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==",
+ "node_modules/@babel/helpers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
+ "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "lodash": "^4.17.13"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
- }
- },
- "@babel/plugin-transform-classes": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz",
- "integrity": "sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w==",
- "dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-define-map": "^7.8.3",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "globals": "^11.1.0"
+ "@babel/template": "^7.27.1",
+ "@babel/types": "^7.27.1"
},
- "dependencies": {
- "@babel/helper-member-expression-to-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
- "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-optimise-call-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
- "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/helper-replace-supers": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz",
- "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==",
- "dev": true,
- "requires": {
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@babel/plugin-transform-computed-properties": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz",
- "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==",
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "@babel/plugin-transform-destructuring": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz",
- "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==",
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz",
+ "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-dotall-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz",
- "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==",
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz",
+ "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==",
"dev": true,
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-duplicate-keys": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz",
- "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==",
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
+ "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-exponentiation-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz",
- "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==",
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz",
+ "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==",
"dev": true,
- "requires": {
- "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/plugin-transform-optional-chaining": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
}
},
- "@babel/plugin-transform-for-of": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz",
- "integrity": "sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A==",
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz",
+ "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-function-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz",
- "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==",
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"dev": true,
- "requires": {
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
+ "engines": {
+ "node": ">=6.9.0"
},
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-literals": {
+ "node_modules/@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz",
- "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-member-expression-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz",
- "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==",
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz",
+ "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-modules-amd": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz",
- "integrity": "sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==",
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
+ "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
"dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-modules-commonjs": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz",
- "integrity": "sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==",
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
+ "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
"dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-simple-access": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-modules-systemjs": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz",
- "integrity": "sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==",
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz",
+ "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==",
"dev": true,
- "requires": {
- "@babel/helper-hoist-variables": "^7.8.3",
- "@babel/helper-module-transforms": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-modules-umd": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz",
- "integrity": "sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==",
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
"dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz",
- "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==",
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz",
+ "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==",
"dev": true,
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-new-target": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz",
- "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==",
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
+ "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-remap-async-to-generator": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-object-super": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz",
- "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.3"
- },
- "dependencies": {
- "@babel/helper-member-expression-to-functions": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
- "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-optimise-call-expression": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
- "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/helper-replace-supers": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz",
- "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==",
- "dev": true,
- "requires": {
- "@babel/helper-member-expression-to-functions": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
+ "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-remap-async-to-generator": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-parameters": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz",
- "integrity": "sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA==",
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
+ "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
"dev": true,
- "requires": {
- "@babel/helper-call-delegate": "^7.8.3",
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-property-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz",
- "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==",
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz",
+ "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-react-display-name": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.7.4.tgz",
- "integrity": "sha512-sBbIvqYkthai0X0vkD2xsAwluBp+LtNHH+/V4a5ydifmTtb8KOVOlrMIk/MYmIc4uTYDnjZUHQildYNo36SRJw==",
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz",
+ "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-react-jsx": {
- "version": "7.7.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.7.tgz",
- "integrity": "sha512-SlPjWPbva2+7/ZJbGcoqjl4LsQaLpKEzxW9hcxU7675s24JmdotJOSJ4cgAbV82W3FcZpHIGmRZIlUL8ayMvjw==",
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz",
+ "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==",
"dev": true,
- "requires": {
- "@babel/helper-builder-react-jsx": "^7.7.4",
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-syntax-jsx": "^7.7.4"
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
}
},
- "@babel/plugin-transform-react-jsx-development": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz",
- "integrity": "sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw==",
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz",
+ "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==",
"dev": true,
- "requires": {
- "@babel/helper-builder-react-jsx-experimental": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-jsx": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/plugin-syntax-jsx": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz",
- "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-compilation-targets": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-replace-supers": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-react-jsx-self": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.7.4.tgz",
- "integrity": "sha512-PWYjSfqrO273mc1pKCRTIJXyqfc9vWYBax88yIhQb+bpw3XChVC7VWS4VwRVs63wFHKxizvGSd00XEr+YB9Q2A==",
+ "node_modules/@babel/plugin-transform-classes/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-syntax-jsx": "^7.7.4"
+ "engines": {
+ "node": ">=4"
}
},
- "@babel/plugin-transform-react-jsx-source": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.7.4.tgz",
- "integrity": "sha512-5ZU9FnPhqtHsOXxutRtXZAzoEJwDaP32QcobbMP1/qt7NYcsCNK8XgzJcJfoEr/ZnzVvUNInNjIW22Z6I8p9mg==",
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz",
+ "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-syntax-jsx": "^7.7.4"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/template": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-regenerator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz",
- "integrity": "sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA==",
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz",
+ "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==",
"dev": true,
- "requires": {
- "regenerator-transform": "^0.14.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-reserved-words": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz",
- "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==",
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz",
+ "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-runtime": {
- "version": "7.7.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz",
- "integrity": "sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A==",
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz",
+ "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==",
"dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.7.4",
- "@babel/helper-plugin-utils": "^7.0.0",
- "resolve": "^1.8.1",
- "semver": "^5.5.1"
- },
- "dependencies": {
- "@babel/helper-module-imports": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz",
- "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.7.4"
- }
- },
- "@babel/types": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
- "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-shorthand-properties": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz",
- "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==",
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz",
+ "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/plugin-transform-spread": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz",
- "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==",
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
+ "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-sticky-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz",
- "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==",
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
+ "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-regex": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-template-literals": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz",
- "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==",
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz",
+ "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==",
"dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-typeof-symbol": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz",
- "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==",
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
+ "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-typescript": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.4.tgz",
- "integrity": "sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w==",
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz",
+ "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==",
"dev": true,
- "requires": {
- "@babel/helper-create-class-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-typescript": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-compilation-targets": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/plugin-transform-unicode-regex": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
- "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==",
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz",
+ "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==",
"dev": true,
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/polyfill": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.8.7.tgz",
- "integrity": "sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w==",
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz",
+ "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==",
"dev": true,
- "requires": {
- "core-js": "^2.6.5",
- "regenerator-runtime": "^0.13.4"
- },
"dependencies": {
- "core-js": {
- "version": "2.6.11",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
- "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
- "dev": true
- },
- "regenerator-runtime": {
- "version": "0.13.4",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz",
- "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==",
- "dev": true
- }
- }
- },
- "@babel/preset-env": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.4.tgz",
- "integrity": "sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.8.4",
- "@babel/helper-compilation-targets": "^7.8.4",
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-proposal-async-generator-functions": "^7.8.3",
- "@babel/plugin-proposal-dynamic-import": "^7.8.3",
- "@babel/plugin-proposal-json-strings": "^7.8.3",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-proposal-object-rest-spread": "^7.8.3",
- "@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
- "@babel/plugin-proposal-optional-chaining": "^7.8.3",
- "@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
- "@babel/plugin-syntax-async-generators": "^7.8.0",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0",
- "@babel/plugin-syntax-json-strings": "^7.8.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0",
- "@babel/plugin-syntax-top-level-await": "^7.8.3",
- "@babel/plugin-transform-arrow-functions": "^7.8.3",
- "@babel/plugin-transform-async-to-generator": "^7.8.3",
- "@babel/plugin-transform-block-scoped-functions": "^7.8.3",
- "@babel/plugin-transform-block-scoping": "^7.8.3",
- "@babel/plugin-transform-classes": "^7.8.3",
- "@babel/plugin-transform-computed-properties": "^7.8.3",
- "@babel/plugin-transform-destructuring": "^7.8.3",
- "@babel/plugin-transform-dotall-regex": "^7.8.3",
- "@babel/plugin-transform-duplicate-keys": "^7.8.3",
- "@babel/plugin-transform-exponentiation-operator": "^7.8.3",
- "@babel/plugin-transform-for-of": "^7.8.4",
- "@babel/plugin-transform-function-name": "^7.8.3",
- "@babel/plugin-transform-literals": "^7.8.3",
- "@babel/plugin-transform-member-expression-literals": "^7.8.3",
- "@babel/plugin-transform-modules-amd": "^7.8.3",
- "@babel/plugin-transform-modules-commonjs": "^7.8.3",
- "@babel/plugin-transform-modules-systemjs": "^7.8.3",
- "@babel/plugin-transform-modules-umd": "^7.8.3",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3",
- "@babel/plugin-transform-new-target": "^7.8.3",
- "@babel/plugin-transform-object-super": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.8.4",
- "@babel/plugin-transform-property-literals": "^7.8.3",
- "@babel/plugin-transform-regenerator": "^7.8.3",
- "@babel/plugin-transform-reserved-words": "^7.8.3",
- "@babel/plugin-transform-shorthand-properties": "^7.8.3",
- "@babel/plugin-transform-spread": "^7.8.3",
- "@babel/plugin-transform-sticky-regex": "^7.8.3",
- "@babel/plugin-transform-template-literals": "^7.8.3",
- "@babel/plugin-transform-typeof-symbol": "^7.8.4",
- "@babel/plugin-transform-unicode-regex": "^7.8.3",
- "@babel/types": "^7.8.3",
- "browserslist": "^4.8.5",
- "core-js-compat": "^3.6.2",
- "invariant": "^2.2.2",
- "levenary": "^1.1.1",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
- }
- },
- "@babel/plugin-proposal-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-transform-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz",
- "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "browserslist": {
- "version": "4.8.6",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz",
- "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001023",
- "electron-to-chromium": "^1.3.341",
- "node-releases": "^1.1.47"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001023",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
- "integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==",
- "dev": true
- },
- "electron-to-chromium": {
- "version": "1.3.344",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
- "integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/preset-modules": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz",
- "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==",
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz",
+ "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
- "@babel/plugin-transform-dotall-regex": "^7.4.4",
- "@babel/types": "^7.4.4",
- "esutils": "^2.0.2"
- },
"dependencies": {
- "@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/preset-react": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.7.4.tgz",
- "integrity": "sha512-j+vZtg0/8pQr1H8wKoaJyGL2IEk3rG/GIvua7Sec7meXVIvGycihlGMx5xcU00kqCJbwzHs18xTu3YfREOqQ+g==",
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz",
+ "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-transform-react-display-name": "^7.7.4",
- "@babel/plugin-transform-react-jsx": "^7.7.4",
- "@babel/plugin-transform-react-jsx-self": "^7.7.4",
- "@babel/plugin-transform-react-jsx-source": "^7.7.4"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/preset-typescript": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz",
- "integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==",
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz",
+ "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-transform-typescript": "^7.9.0"
- },
"dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- }
+ "@babel/helper-module-transforms": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/register": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.9.0.tgz",
- "integrity": "sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q==",
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz",
+ "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==",
"dev": true,
- "requires": {
- "find-cache-dir": "^2.0.0",
- "lodash": "^4.17.13",
- "make-dir": "^2.1.0",
- "pirates": "^4.0.0",
- "source-map-support": "^0.5.16"
- },
"dependencies": {
- "make-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
- "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
- "dev": true,
- "requires": {
- "pify": "^4.0.1",
- "semver": "^5.6.0"
- }
- },
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- },
- "source-map-support": {
- "version": "0.5.16",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
- "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- }
+ "@babel/helper-module-transforms": "^7.26.0",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/runtime": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz",
- "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==",
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz",
+ "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==",
"dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.2"
- },
+ "license": "MIT",
"dependencies": {
- "regenerator-runtime": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
- "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
- "dev": true
- }
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/runtime-corejs2": {
- "version": "7.4.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.3.tgz",
- "integrity": "sha512-anTLTF7IK8Hd5f73zpPzt875I27UaaTWARJlfMGgnmQhvEe1uNHQRKBUbXL0Gc0VEYiVzsHsTPso5XdK8NGvFg==",
- "requires": {
- "core-js": "^2.6.5",
- "regenerator-runtime": "^0.13.2"
- },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz",
+ "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==",
+ "dev": true,
"dependencies": {
- "core-js": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
- "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
- },
- "regenerator-runtime": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
- "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
- }
+ "@babel/helper-module-transforms": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/runtime-corejs3": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.8.7.tgz",
- "integrity": "sha512-sc7A+H4I8kTd7S61dgB9RomXu/C+F4IrRr4Ytze4dnfx7AXEpCrejSNpjx7vq6y/Bak9S6Kbk65a/WgMLtg43Q==",
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz",
+ "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==",
"dev": true,
- "requires": {
- "core-js-pure": "^3.0.0",
- "regenerator-runtime": "^0.13.4"
- },
"dependencies": {
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
- "dev": true
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- }
- }
- },
- "@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
- "dev": true
- },
- "@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz",
+ "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@babel/types": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0.tgz",
- "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==",
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz",
+ "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==",
"dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.10",
- "to-fast-properties": "^2.0.0"
- },
"dependencies": {
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@bahmutov/data-driven": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@bahmutov/data-driven/-/data-driven-1.0.0.tgz",
- "integrity": "sha512-YqW3hPS0RXriqjcCrLOTJj+LWe3c8JpwlL83k1ka1Q8U05ZjAKbGQZYeTzUd0NFEnnfPtsUiKGpFEBJG6kFuvg==",
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
+ "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
"dev": true,
- "requires": {
- "check-more-types": "2.24.0",
- "lazy-ass": "1.6.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@cypress/listr-verbose-renderer": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
- "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=",
- "dev": true,
- "requires": {
- "chalk": "^1.1.3",
- "cli-cursor": "^1.0.2",
- "date-fns": "^1.27.2",
- "figures": "^1.7.0"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "cli-cursor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
- "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
- "dev": true,
- "requires": {
- "restore-cursor": "^1.0.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "figures": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5",
- "object-assign": "^4.1.0"
- }
- },
- "onetime": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
- "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
- "dev": true
- },
- "restore-cursor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
- "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
- "dev": true,
- "requires": {
- "exit-hook": "^1.0.0",
- "onetime": "^1.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- }
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
+ "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/plugin-transform-parameters": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@cypress/request": {
- "version": "2.88.5",
- "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
- "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==",
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
+ "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
"dev": true,
- "requires": {
- "aws-sign2": "~0.7.0",
- "aws4": "^1.8.0",
- "caseless": "~0.12.0",
- "combined-stream": "~1.0.6",
- "extend": "~3.0.2",
- "forever-agent": "~0.6.1",
- "form-data": "~2.3.2",
- "har-validator": "~5.1.3",
- "http-signature": "~1.2.0",
- "is-typedarray": "~1.0.0",
- "isstream": "~0.1.2",
- "json-stringify-safe": "~5.0.1",
- "mime-types": "~2.1.19",
- "oauth-sign": "~0.9.0",
- "performance-now": "^2.1.0",
- "qs": "~6.5.2",
- "safe-buffer": "^5.1.2",
- "tough-cookie": "~2.5.0",
- "tunnel-agent": "^0.6.0",
- "uuid": "^3.3.2"
- },
"dependencies": {
- "mime-db": {
- "version": "1.44.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
- "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.27",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
- "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
- "dev": true,
- "requires": {
- "mime-db": "1.44.0"
- }
- },
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-replace-supers": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@cypress/xvfb": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
- "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
+ "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
"dev": true,
- "requires": {
- "debug": "^3.1.0",
- "lodash.once": "^4.1.1"
- },
"dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/cache": {
- "version": "10.0.29",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
- "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==",
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz",
+ "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==",
"dev": true,
- "requires": {
- "@emotion/sheet": "0.9.4",
- "@emotion/stylis": "0.8.5",
- "@emotion/utils": "0.11.3",
- "@emotion/weak-memoize": "0.2.5"
- },
"dependencies": {
- "@emotion/stylis": {
- "version": "0.8.5",
- "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
- "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/core": {
- "version": "10.0.28",
- "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz",
- "integrity": "sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==",
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz",
+ "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.5.5",
- "@emotion/cache": "^10.0.27",
- "@emotion/css": "^10.0.27",
- "@emotion/serialize": "^0.11.15",
- "@emotion/sheet": "0.9.4",
- "@emotion/utils": "0.11.3"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/css": {
- "version": "10.0.27",
- "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
- "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz",
+ "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==",
"dev": true,
- "requires": {
- "@emotion/serialize": "^0.11.15",
- "@emotion/utils": "0.11.3",
- "babel-plugin-emotion": "^10.0.27"
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/hash": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
- "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
- "dev": true
- },
- "@emotion/is-prop-valid": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz",
- "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==",
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz",
+ "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==",
"dev": true,
- "requires": {
- "@emotion/memoize": "0.7.1"
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/memoize": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
- "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==",
- "dev": true
- },
- "@emotion/serialize": {
- "version": "0.11.16",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
- "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==",
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz",
+ "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==",
"dev": true,
- "requires": {
- "@emotion/hash": "0.8.0",
- "@emotion/memoize": "0.7.4",
- "@emotion/unitless": "0.7.5",
- "@emotion/utils": "0.11.3",
- "csstype": "^2.5.7"
- },
"dependencies": {
- "@emotion/memoize": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
- "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
- "dev": true
- },
- "@emotion/unitless": {
- "version": "0.7.5",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
- "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
- "dev": true
- }
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@emotion/sheet": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz",
- "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==",
- "dev": true
- },
- "@emotion/stylis": {
- "version": "0.8.5",
- "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
- "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==",
- "dev": true
- },
- "@emotion/unitless": {
- "version": "0.7.5",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
- "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
- "dev": true
- },
- "@emotion/utils": {
- "version": "0.11.3",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz",
- "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==",
- "dev": true
- },
- "@emotion/weak-memoize": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
- "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==",
- "dev": true
- },
- "@hapi/address": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
- "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==",
- "dev": true
- },
- "@hapi/bourne": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz",
- "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==",
- "dev": true
- },
- "@hapi/formula": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-1.2.0.tgz",
- "integrity": "sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA=="
- },
- "@hapi/hoek": {
- "version": "8.2.4",
- "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.2.4.tgz",
- "integrity": "sha512-Ze5SDNt325yZvNO7s5C4fXDscjJ6dcqLFXJQ/M7dZRQCewuDj2iDUuBi6jLQt+APbW9RjjVEvLr35FXuOEqjow=="
- },
- "@hapi/joi": {
- "version": "16.1.8",
- "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-16.1.8.tgz",
- "integrity": "sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==",
- "requires": {
- "@hapi/address": "^2.1.2",
- "@hapi/formula": "^1.2.0",
- "@hapi/hoek": "^8.2.4",
- "@hapi/pinpoint": "^1.0.2",
- "@hapi/topo": "^3.1.3"
- },
+ "node_modules/@babel/plugin-transform-react-constant-elements": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.29.7.tgz",
+ "integrity": "sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@hapi/address": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
- "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ=="
- }
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@hapi/pinpoint": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-1.0.2.tgz",
- "integrity": "sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ=="
- },
- "@hapi/topo": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.3.tgz",
- "integrity": "sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ==",
- "requires": {
- "@hapi/hoek": "8.x.x"
+ "node_modules/@babel/plugin-transform-react-display-name": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz",
+ "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@istanbuljs/load-nyc-config": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz",
- "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==",
+ "node_modules/@babel/plugin-transform-react-jsx": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
+ "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
"dev": true,
- "requires": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
- "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true
- }
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/plugin-syntax-jsx": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@istanbuljs/schema": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
- "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
- "dev": true
- },
- "@mapbox/react-click-to-select": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@mapbox/react-click-to-select/-/react-click-to-select-2.2.0.tgz",
- "integrity": "sha512-L42P/vM4IP8NfvF5EEm0qRR1GjriHj+KFnuGo6U2gP1qdGFqZxj7VloHX5TGJPsQQVJH+BmjWAmF9RebeQ4ubQ==",
- "dev": true
- },
- "@mikaelkristiansson/domready": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/@mikaelkristiansson/domready/-/domready-1.0.10.tgz",
- "integrity": "sha512-6cDuZeKSCSJ1KvfEQ25Y8OXUjqDJZ+HgUs6dhASWbAX8fxVraTfPsSeRe2bN+4QJDsgUaXaMWBYfRomCr04GGg==",
- "dev": true
- },
- "@mrmlnc/readdir-enhanced": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
- "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==",
+ "node_modules/@babel/plugin-transform-react-jsx-development": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz",
+ "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==",
"dev": true,
- "requires": {
- "call-me-maybe": "^1.0.1",
- "glob-to-regexp": "^0.3.0"
+ "dependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@nodelib/fs.scandir": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz",
- "integrity": "sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==",
+ "node_modules/@babel/plugin-transform-react-pure-annotations": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz",
+ "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==",
"dev": true,
- "requires": {
- "@nodelib/fs.stat": "2.0.2",
- "run-parallel": "^1.1.9"
- },
"dependencies": {
- "@nodelib/fs.stat": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz",
- "integrity": "sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==",
- "dev": true
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@nodelib/fs.stat": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
- "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
- "dev": true
- },
- "@nodelib/fs.walk": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz",
- "integrity": "sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==",
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz",
+ "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==",
"dev": true,
- "requires": {
- "@nodelib/fs.scandir": "2.1.2",
- "fastq": "^1.6.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "regenerator-transform": "^0.15.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/auth-token": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz",
- "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==",
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz",
+ "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==",
"dev": true,
- "requires": {
- "@octokit/types": "^2.0.0"
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@octokit/endpoint": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.0.tgz",
- "integrity": "sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A==",
- "dev": true,
- "requires": {
- "@octokit/types": "^2.0.0",
- "is-plain-object": "^3.0.0",
- "universal-user-agent": "^5.0.0"
- },
- "dependencies": {
- "is-plain-object": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
- "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
- "dev": true,
- "requires": {
- "isobject": "^4.0.0"
- }
- },
- "isobject": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
- "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==",
- "dev": true
- },
- "universal-user-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
- "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
- "dev": true,
- "requires": {
- "os-name": "^3.1.0"
- }
- }
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz",
+ "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/plugin-paginate-rest": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz",
- "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==",
+ "node_modules/@babel/plugin-transform-runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz",
+ "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==",
"dev": true,
- "requires": {
- "@octokit/types": "^2.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "babel-plugin-polyfill-corejs2": "^0.4.14",
+ "babel-plugin-polyfill-corejs3": "^0.13.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.5",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/plugin-request-log": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz",
- "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==",
- "dev": true
+ "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz",
+ "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "core-js-compat": "^3.43.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
},
- "@octokit/plugin-rest-endpoint-methods": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz",
- "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==",
+ "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "@octokit/types": "^2.0.1",
- "deprecation": "^2.3.1"
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "@octokit/request": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.0.tgz",
- "integrity": "sha512-uAJO6GI8z8VHBqtY7VTL9iFy1Y+UTp5ShpI97tY5z0qBfYKE9rZCRsCm23VmF00x+IoNJ7a0nuVITs/+wS9/mg==",
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz",
+ "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==",
"dev": true,
- "requires": {
- "@octokit/endpoint": "^6.0.0",
- "@octokit/request-error": "^2.0.0",
- "@octokit/types": "^2.8.2",
- "deprecation": "^2.0.0",
- "is-plain-object": "^3.0.0",
- "node-fetch": "^2.3.0",
- "once": "^1.4.0",
- "universal-user-agent": "^5.0.0"
- },
- "dependencies": {
- "@octokit/request-error": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz",
- "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==",
- "dev": true,
- "requires": {
- "@octokit/types": "^2.0.0",
- "deprecation": "^2.0.0",
- "once": "^1.4.0"
- }
- },
- "is-plain-object": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
- "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
- "dev": true,
- "requires": {
- "isobject": "^4.0.0"
- }
- },
- "isobject": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
- "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==",
- "dev": true
- },
- "universal-user-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
- "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
- "dev": true,
- "requires": {
- "os-name": "^3.1.0"
- }
- }
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/request-error": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
- "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz",
+ "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==",
"dev": true,
- "requires": {
- "@octokit/types": "^2.0.0",
- "deprecation": "^2.0.0",
- "once": "^1.4.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/rest": {
- "version": "16.43.1",
- "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz",
- "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==",
- "dev": true,
- "requires": {
- "@octokit/auth-token": "^2.4.0",
- "@octokit/plugin-paginate-rest": "^1.1.1",
- "@octokit/plugin-request-log": "^1.0.0",
- "@octokit/plugin-rest-endpoint-methods": "2.4.0",
- "@octokit/request": "^5.2.0",
- "@octokit/request-error": "^1.0.2",
- "atob-lite": "^2.0.0",
- "before-after-hook": "^2.0.0",
- "btoa-lite": "^1.0.0",
- "deprecation": "^2.0.0",
- "lodash.get": "^4.4.2",
- "lodash.set": "^4.3.2",
- "lodash.uniq": "^4.5.0",
- "octokit-pagination-methods": "^1.1.0",
- "once": "^1.4.0",
- "universal-user-agent": "^4.0.0"
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz",
+ "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@octokit/types": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.10.0.tgz",
- "integrity": "sha512-0/NN22MgQvNNgMjTwzWUzcIfFfks3faqiP1D1oQQz49KYeOWc+KkRG9ASbAPurrAnOaDiqnnuDYzhNT9cq4e8Q==",
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
+ "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
"dev": true,
- "requires": {
- "@types/node": ">= 8"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@pieh/friendly-errors-webpack-plugin": {
- "version": "1.7.0-chalk-2",
- "resolved": "https://registry.npmjs.org/@pieh/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0-chalk-2.tgz",
- "integrity": "sha512-65+vYGuDkHBCWWjqzzR/Ck318+d6yTI00EqII9qe3aPD1J3Olhvw0X38uM5moQb1PK/ksDXwSoPGt/5QhCiotw==",
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
+ "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
"dev": true,
- "requires": {
- "chalk": "^2.4.2",
- "error-stack-parser": "^2.0.0",
- "string-width": "^2.0.0",
- "strip-ansi": "^3"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
},
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz",
+ "integrity": "sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==",
+ "dev": true,
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "@babel/helper-annotate-as-pure": "^7.25.9",
+ "@babel/helper-create-class-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+ "@babel/plugin-syntax-typescript": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@pmmmwh/react-refresh-webpack-plugin": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.2.0.tgz",
- "integrity": "sha512-rjdNzcWroULJeD/Y0+eETy9LhM7c5tbPF+wqT5G680rwDkh3iothIPEqGAuEE2WJlXEaAq293aO6ySzsIU518Q==",
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz",
+ "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==",
"dev": true,
- "requires": {
- "ansi-html": "^0.0.7",
- "error-stack-parser": "^2.0.4",
- "html-entities": "^1.2.1",
- "lodash.debounce": "^4.0.8",
- "react-dev-utils": "^9.1.0"
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
},
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz",
+ "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==",
+ "dev": true,
"dependencies": {
- "@babel/code-frame": {
- "version": "7.5.5",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
- "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.0.0"
- }
- },
- "ansi-escapes": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
- "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
- "dev": true
- },
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "array-union": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
- "dev": true,
- "requires": {
- "array-uniq": "^1.0.1"
- }
- },
- "browserslist": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz",
- "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30000989",
- "electron-to-chromium": "^1.3.247",
- "node-releases": "^1.1.29"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "detect-port-alt": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz",
- "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==",
- "dev": true,
- "requires": {
- "address": "^1.0.1",
- "debug": "^2.6.0"
- }
- },
- "dir-glob": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
- "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==",
- "dev": true,
- "requires": {
- "arrify": "^1.0.1",
- "path-type": "^3.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "globby": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz",
- "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==",
- "dev": true,
- "requires": {
- "array-union": "^1.0.1",
- "dir-glob": "2.0.0",
- "fast-glob": "^2.0.2",
- "glob": "^7.1.2",
- "ignore": "^3.3.5",
- "pify": "^3.0.0",
- "slash": "^1.0.0"
- }
- },
- "ignore": {
- "version": "3.3.10",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
- "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
- "dev": true
- },
- "inquirer": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz",
- "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^3.2.0",
- "chalk": "^2.4.2",
- "cli-cursor": "^2.1.0",
- "cli-width": "^2.0.0",
- "external-editor": "^3.0.3",
- "figures": "^2.0.0",
- "lodash": "^4.17.12",
- "mute-stream": "0.0.7",
- "run-async": "^2.2.0",
- "rxjs": "^6.4.0",
- "string-width": "^2.1.0",
- "strip-ansi": "^5.1.0",
- "through": "^2.3.6"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "path-type": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
- "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
- "dev": true,
- "requires": {
- "pify": "^3.0.0"
- }
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "react-dev-utils": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz",
- "integrity": "sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "7.5.5",
- "address": "1.1.2",
- "browserslist": "4.7.0",
- "chalk": "2.4.2",
- "cross-spawn": "6.0.5",
- "detect-port-alt": "1.1.6",
- "escape-string-regexp": "1.0.5",
- "filesize": "3.6.1",
- "find-up": "3.0.0",
- "fork-ts-checker-webpack-plugin": "1.5.0",
- "global-modules": "2.0.0",
- "globby": "8.0.2",
- "gzip-size": "5.1.1",
- "immer": "1.10.0",
- "inquirer": "6.5.0",
- "is-root": "2.1.0",
- "loader-utils": "1.2.3",
- "open": "^6.3.0",
- "pkg-up": "2.0.0",
- "react-error-overlay": "^6.0.3",
- "recursive-readdir": "2.2.2",
- "shell-quote": "1.7.2",
- "sockjs-client": "1.4.0",
- "strip-ansi": "5.2.0",
- "text-table": "0.2.0"
- }
- },
- "react-error-overlay": {
- "version": "6.0.6",
- "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.6.tgz",
- "integrity": "sha512-Yzpno3enVzSrSCnnljmr4b/2KUQSMZaPuqmS26t9k4nW7uwJk6STWmH9heNjPuvqUTO3jOSPkHoKgO4+Dw7uIw==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "shell-quote": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
- "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
- "dev": true
- },
- "slash": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
- "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
- }
- },
- "@popmotion/easing": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@popmotion/easing/-/easing-1.0.2.tgz",
- "integrity": "sha512-IkdW0TNmRnWTeWI7aGQIVDbKXPWHVEYdGgd5ZR4SH/Ty/61p63jCjrPxX1XrR7IGkl08bjhJROStD7j+RKgoIw==",
- "dev": true
- },
- "@popmotion/popcorn": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/@popmotion/popcorn/-/popcorn-0.4.4.tgz",
- "integrity": "sha512-jYO/8319fKoNLMlY4ZJPiPu8Ea8occYwRZhxpaNn/kZsK4QG2E7XFlXZMJBsTWDw7I1i0uaqyC4zn1nwEezLzg==",
- "dev": true,
- "requires": {
- "@popmotion/easing": "^1.0.1",
- "framesync": "^4.0.1",
- "hey-listen": "^1.0.8",
- "style-value-types": "^3.1.7",
- "tslib": "^1.10.0"
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
},
- "dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@reach/router": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.3.tgz",
- "integrity": "sha512-gOIAiFhWdiVGSVjukKeNKkCRBLmnORoTPyBihI/jLunICPgxdP30DroAvPQuf1eVfQbfGJQDJkwhJXsNPMnVWw==",
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz",
+ "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==",
"dev": true,
- "requires": {
- "create-react-context": "0.3.0",
- "invariant": "^2.2.3",
- "prop-types": "^15.6.1",
- "react-lifecycles-compat": "^3.0.4"
- },
"dependencies": {
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- }
- }
- },
- "@samverschueren/stream-to-observable": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
- "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==",
- "dev": true,
- "requires": {
- "any-observable": "^0.3.0"
- }
- },
- "@sentry/apm": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.15.5.tgz",
- "integrity": "sha512-2PyifsiQdvFEQhbL7tQnCKGLOO1JtZeqso3bc6ARJBvKxM77mtyMo/D0C2Uzt9sXCYiALhQ1rbB1aY8iYyglpg==",
- "requires": {
- "@sentry/browser": "5.15.5",
- "@sentry/hub": "5.15.5",
- "@sentry/minimal": "5.15.5",
- "@sentry/types": "5.15.5",
- "@sentry/utils": "5.15.5",
- "tslib": "^1.9.3"
- }
- },
- "@sentry/browser": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.15.5.tgz",
- "integrity": "sha512-rqDvjk/EvogfdbZ4TiEpxM/lwpPKmq23z9YKEO4q81+1SwJNua53H60dOk9HpRU8nOJ1g84TMKT2Ov8H7sqDWA==",
- "requires": {
- "@sentry/core": "5.15.5",
- "@sentry/types": "5.15.5",
- "@sentry/utils": "5.15.5",
- "tslib": "^1.9.3"
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@sentry/core": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.15.5.tgz",
- "integrity": "sha512-enxBLv5eibBMqcWyr+vApqeix8uqkfn0iGsD3piKvoMXCgKsrfMwlb/qo9Ox0lKr71qIlZVt+9/A2vZohdgnlg==",
- "requires": {
- "@sentry/hub": "5.15.5",
- "@sentry/minimal": "5.15.5",
- "@sentry/types": "5.15.5",
- "@sentry/utils": "5.15.5",
- "tslib": "^1.9.3"
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz",
+ "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
}
},
- "@sentry/hub": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.15.5.tgz",
- "integrity": "sha512-zX9o49PcNIVMA4BZHe//GkbQ4Jx+nVofqU/Il32/IbwKhcpPlhGX3c1sOVQo4uag3cqd/JuQsk+DML9TKkN0Lw==",
- "requires": {
- "@sentry/types": "5.15.5",
- "@sentry/utils": "5.15.5",
- "tslib": "^1.9.3"
+ "node_modules/@babel/preset-env": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
+ "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.26.0",
+ "@babel/helper-compilation-targets": "^7.25.9",
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-validator-option": "^7.25.9",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.26.0",
+ "@babel/plugin-syntax-import-attributes": "^7.26.0",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.25.9",
+ "@babel/plugin-transform-async-generator-functions": "^7.25.9",
+ "@babel/plugin-transform-async-to-generator": "^7.25.9",
+ "@babel/plugin-transform-block-scoped-functions": "^7.25.9",
+ "@babel/plugin-transform-block-scoping": "^7.25.9",
+ "@babel/plugin-transform-class-properties": "^7.25.9",
+ "@babel/plugin-transform-class-static-block": "^7.26.0",
+ "@babel/plugin-transform-classes": "^7.25.9",
+ "@babel/plugin-transform-computed-properties": "^7.25.9",
+ "@babel/plugin-transform-destructuring": "^7.25.9",
+ "@babel/plugin-transform-dotall-regex": "^7.25.9",
+ "@babel/plugin-transform-duplicate-keys": "^7.25.9",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9",
+ "@babel/plugin-transform-dynamic-import": "^7.25.9",
+ "@babel/plugin-transform-exponentiation-operator": "^7.25.9",
+ "@babel/plugin-transform-export-namespace-from": "^7.25.9",
+ "@babel/plugin-transform-for-of": "^7.25.9",
+ "@babel/plugin-transform-function-name": "^7.25.9",
+ "@babel/plugin-transform-json-strings": "^7.25.9",
+ "@babel/plugin-transform-literals": "^7.25.9",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
+ "@babel/plugin-transform-member-expression-literals": "^7.25.9",
+ "@babel/plugin-transform-modules-amd": "^7.25.9",
+ "@babel/plugin-transform-modules-commonjs": "^7.25.9",
+ "@babel/plugin-transform-modules-systemjs": "^7.25.9",
+ "@babel/plugin-transform-modules-umd": "^7.25.9",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9",
+ "@babel/plugin-transform-new-target": "^7.25.9",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
+ "@babel/plugin-transform-numeric-separator": "^7.25.9",
+ "@babel/plugin-transform-object-rest-spread": "^7.25.9",
+ "@babel/plugin-transform-object-super": "^7.25.9",
+ "@babel/plugin-transform-optional-catch-binding": "^7.25.9",
+ "@babel/plugin-transform-optional-chaining": "^7.25.9",
+ "@babel/plugin-transform-parameters": "^7.25.9",
+ "@babel/plugin-transform-private-methods": "^7.25.9",
+ "@babel/plugin-transform-private-property-in-object": "^7.25.9",
+ "@babel/plugin-transform-property-literals": "^7.25.9",
+ "@babel/plugin-transform-regenerator": "^7.25.9",
+ "@babel/plugin-transform-regexp-modifiers": "^7.26.0",
+ "@babel/plugin-transform-reserved-words": "^7.25.9",
+ "@babel/plugin-transform-shorthand-properties": "^7.25.9",
+ "@babel/plugin-transform-spread": "^7.25.9",
+ "@babel/plugin-transform-sticky-regex": "^7.25.9",
+ "@babel/plugin-transform-template-literals": "^7.25.9",
+ "@babel/plugin-transform-typeof-symbol": "^7.25.9",
+ "@babel/plugin-transform-unicode-escapes": "^7.25.9",
+ "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
+ "@babel/plugin-transform-unicode-regex": "^7.25.9",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.25.9",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.10",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
+ "babel-plugin-polyfill-regenerator": "^0.6.1",
+ "core-js-compat": "^3.38.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
}
},
- "@sentry/minimal": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.15.5.tgz",
- "integrity": "sha512-zQkkJ1l9AjmU/Us5IrOTzu7bic4sTPKCatptXvLSTfyKW7N6K9MPIIFeSpZf9o1yM2sRYdK7GV08wS2eCT3JYw==",
- "requires": {
- "@sentry/hub": "5.15.5",
- "@sentry/types": "5.15.5",
- "tslib": "^1.9.3"
+ "node_modules/@babel/preset-react": {
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz",
+ "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-validator-option": "^7.25.9",
+ "@babel/plugin-transform-react-display-name": "^7.25.9",
+ "@babel/plugin-transform-react-jsx": "^7.25.9",
+ "@babel/plugin-transform-react-jsx-development": "^7.25.9",
+ "@babel/plugin-transform-react-pure-annotations": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@sentry/node": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.15.5.tgz",
- "integrity": "sha512-BK0iTOiiIM0UnydLeT/uUBY1o1Sp85aqwaQRMfZbjMCsgXERLNGvzzV68FDH1cyC1nR6dREK3Gs8bxS4S54aLQ==",
- "requires": {
- "@sentry/apm": "5.15.5",
- "@sentry/core": "5.15.5",
- "@sentry/hub": "5.15.5",
- "@sentry/types": "5.15.5",
- "@sentry/utils": "5.15.5",
- "cookie": "^0.3.1",
- "https-proxy-agent": "^4.0.0",
- "lru_map": "^0.3.3",
- "tslib": "^1.9.3"
- },
+ "node_modules/@babel/preset-typescript": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz",
+ "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==",
+ "dev": true,
"dependencies": {
- "agent-base": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
- "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "https-proxy-agent": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
- "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
- "requires": {
- "agent-base": "5",
- "debug": "4"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
+ "@babel/helper-plugin-utils": "^7.25.9",
+ "@babel/helper-validator-option": "^7.25.9",
+ "@babel/plugin-syntax-jsx": "^7.25.9",
+ "@babel/plugin-transform-modules-commonjs": "^7.25.9",
+ "@babel/plugin-transform-typescript": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "@sentry/types": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.15.5.tgz",
- "integrity": "sha512-F9A5W7ucgQLJUG4LXw1ZIy4iLevrYZzbeZ7GJ09aMlmXH9PqGThm1t5LSZlVpZvUfQ2rYA8NU6BdKJSt7B5LPw=="
- },
- "@sentry/utils": {
- "version": "5.15.5",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.15.5.tgz",
- "integrity": "sha512-Nl9gl/MGnzSkuKeo3QaefoD/OJrFLB8HmwQ7HUbTXb6E7yyEzNKAQMHXGkwNAjbdYyYbd42iABP6Y5F/h39NtA==",
- "requires": {
- "@sentry/types": "5.15.5",
- "tslib": "^1.9.3"
+ "node_modules/@babel/runtime": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
+ "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@shields_io/camp": {
- "version": "18.0.0",
- "resolved": "https://registry.npmjs.org/@shields_io/camp/-/camp-18.0.0.tgz",
- "integrity": "sha512-faQpvSVqqFX3LfRRdGQQHgXO1s2hsK+QWCZWXdM+3226fdikRCPT+O1YPyDMZdrG/vP5sL9PdOvajgGNwLlU6A==",
- "requires": {
- "cookies": "~0.7.1",
- "fleau": "~16.2.0",
- "formidable": "~1.2.0",
- "multilog": "~14.11.22",
- "socket.io": "^2.0.4",
- "spdy": "^3.4.7",
- "ws": "^6.1.0"
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
- "dev": true
- },
- "@sinonjs/commons": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz",
- "integrity": "sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg==",
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
- "requires": {
- "type-detect": "4.0.8"
- },
+ "license": "MIT",
"dependencies": {
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- }
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@sinonjs/formatio": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-4.0.1.tgz",
- "integrity": "sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw==",
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"dev": true,
- "requires": {
- "@sinonjs/commons": "^1",
- "@sinonjs/samsam": "^4.2.0"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
}
},
- "@sinonjs/samsam": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-4.2.2.tgz",
- "integrity": "sha512-z9o4LZUzSD9Hl22zV38aXNykgFeVj8acqfFabCY6FY83n/6s/XwNJyYYldz6/9lBJanpno9h+oL6HTISkviweA==",
+ "node_modules/@bahmutov/data-driven": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@bahmutov/data-driven/-/data-driven-1.0.0.tgz",
+ "integrity": "sha512-YqW3hPS0RXriqjcCrLOTJj+LWe3c8JpwlL83k1ka1Q8U05ZjAKbGQZYeTzUd0NFEnnfPtsUiKGpFEBJG6kFuvg==",
"dev": true,
- "requires": {
- "@sinonjs/commons": "^1.6.0",
- "lodash.get": "^4.4.2",
- "type-detect": "^4.0.8"
- },
"dependencies": {
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- }
+ "check-more-types": "2.24.0",
+ "lazy-ass": "1.6.0"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "@sinonjs/text-encoding": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
- "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
- "dev": true
- },
- "@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz",
+ "integrity": "sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==",
"dev": true,
- "requires": {
- "defer-to-connect": "^1.0.1"
+ "engines": {
+ "node": ">=18"
}
},
- "@types/blob-util": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@types/blob-util/-/blob-util-1.3.3.tgz",
- "integrity": "sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==",
- "dev": true
- },
- "@types/bluebird": {
- "version": "3.5.29",
- "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.29.tgz",
- "integrity": "sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==",
- "dev": true
- },
- "@types/chai": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz",
- "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==",
- "dev": true
- },
- "@types/chai-enzyme": {
- "version": "0.6.7",
- "resolved": "https://registry.npmjs.org/@types/chai-enzyme/-/chai-enzyme-0.6.7.tgz",
- "integrity": "sha512-vV64mBF86MfmjaIijZY/3iPaJ9SwoXx35wt//KPXKicKtYb2K7xoh/ocXOXGnqRRszTTJSDPMHoJKyeLNx0rYQ==",
+ "node_modules/@borewit/text-codec": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz",
+ "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==",
"dev": true,
- "requires": {
- "@types/chai": "*",
- "@types/cheerio": "*",
- "@types/enzyme": "*",
- "@types/react": "*"
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
}
},
- "@types/chai-jquery": {
- "version": "1.1.40",
- "resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.40.tgz",
- "integrity": "sha512-mCNEZ3GKP7T7kftKeIs7QmfZZQM7hslGSpYzKbOlR2a2HCFf9ph4nlMRA9UnuOETeOQYJVhJQK7MwGqNZVyUtQ==",
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
"dev": true,
- "requires": {
- "@types/chai": "*",
- "@types/jquery": "*"
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.1.90"
}
},
- "@types/cheerio": {
- "version": "0.22.12",
- "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.12.tgz",
- "integrity": "sha512-aczowyAJNfrkBV+HS8DyAA87OnvkqGrrOmm5s7V6Jbgimzv/1ZoAy91cLJX8GQrUS60KufD7EIzA2LbK8HV4hg==",
+ "node_modules/@csstools/cascade-layer-name-parser": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz",
+ "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==",
"dev": true,
- "requires": {
- "@types/node": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
- "@types/color-name": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
- "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
- },
- "@types/configstore": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-2.1.1.tgz",
- "integrity": "sha1-zR6FU2M60xhcPy8jns/10mQ+krY=",
- "dev": true
- },
- "@types/debug": {
- "version": "0.0.30",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-0.0.30.tgz",
- "integrity": "sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==",
- "dev": true
- },
- "@types/enzyme": {
- "version": "3.10.5",
- "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz",
- "integrity": "sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==",
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
"dev": true,
- "requires": {
- "@types/cheerio": "*",
- "@types/react": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
}
},
- "@types/eslint-visitor-keys": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
- "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
- "dev": true
- },
- "@types/events": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
- "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
- "dev": true
- },
- "@types/get-port": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@types/get-port/-/get-port-3.2.0.tgz",
- "integrity": "sha512-TiNg8R1kjDde5Pub9F9vCwZA/BNW9HeXP5b9j7Qucqncy/McfPZ6xze/EyBdXS5FhMIGN6Fx3vg75l5KHy3V1Q==",
- "dev": true
- },
- "@types/glob": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
- "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
"dev": true,
- "requires": {
- "@types/events": "*",
- "@types/minimatch": "*",
- "@types/node": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
- "@types/history": {
- "version": "4.7.5",
- "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz",
- "integrity": "sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==",
- "dev": true
- },
- "@types/invariant": {
- "version": "2.2.30",
- "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.30.tgz",
- "integrity": "sha512-98fB+yo7imSD2F7PF7GIpELNgtLNgo5wjivu0W5V4jx+KVVJxo6p/qN4zdzSTBWy4/sN3pPyXwnhRSD28QX+ag==",
- "dev": true
- },
- "@types/jquery": {
- "version": "3.3.31",
- "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz",
- "integrity": "sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==",
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
"dev": true,
- "requires": {
- "@types/sizzle": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
- "@types/json-schema": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
- "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
- "dev": true
- },
- "@types/json5": {
- "version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
"dev": true,
- "optional": true
- },
- "@types/lodash": {
- "version": "4.14.136",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.136.tgz",
- "integrity": "sha512-0GJhzBdvsW2RUccNHOBkabI8HZVdOXmXbXhuKlDEd5Vv12P7oAVGfomGp3Ne21o5D/qu1WmthlNKFaoZJJeErA==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
},
- "@types/lodash.debounce": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz",
- "integrity": "sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==",
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
"dev": true,
- "requires": {
- "@types/lodash": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
}
},
- "@types/lodash.groupby": {
- "version": "4.6.6",
- "resolved": "https://registry.npmjs.org/@types/lodash.groupby/-/lodash.groupby-4.6.6.tgz",
- "integrity": "sha512-kwg3T7Ia63KtDNoQQR8hKrLHCAgrH4I44l5uEMuA6JCbj7DiSccaV4tNV1vbjtAOpX990SolVthJCmBVtRVRgw==",
+ "node_modules/@csstools/media-query-list-parser": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz",
+ "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==",
"dev": true,
- "requires": {
- "@types/lodash": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
- "@types/minimatch": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
- "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
- "dev": true
- },
- "@types/mkdirp": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz",
- "integrity": "sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==",
+ "node_modules/@csstools/postcss-alpha-function": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz",
+ "integrity": "sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w==",
"dev": true,
- "requires": {
- "@types/node": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/mocha": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz",
- "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==",
- "dev": true
- },
- "@types/node": {
- "version": "13.13.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz",
- "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==",
- "dev": true
- },
- "@types/normalize-package-data": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
- "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
- "dev": true
- },
- "@types/parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
- "dev": true
- },
- "@types/prop-types": {
- "version": "15.7.1",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz",
- "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==",
- "dev": true
- },
- "@types/reach__router": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.0.tgz",
- "integrity": "sha512-0aL79bFPJzJOJOOMZm2301ErQVaveBdpW88uuavXymUlcYIAOCmI1ujJ2XLH6Mzn76O94eQCHIl1FDzNNKJCYA==",
+ "node_modules/@csstools/postcss-cascade-layers": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz",
+ "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==",
"dev": true,
- "requires": {
- "@types/history": "*",
- "@types/react": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/react": {
- "version": "16.8.23",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.23.tgz",
- "integrity": "sha512-abkEOIeljniUN9qB5onp++g0EY38h7atnDHxwKUFz1r3VH1+yG1OKi2sNPTyObL40goBmfKFpdii2lEzwLX1cA==",
+ "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
"dev": true,
- "requires": {
- "@types/prop-types": "*",
- "csstype": "^2.2.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
}
},
- "@types/react-dom": {
- "version": "16.9.5",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.5.tgz",
- "integrity": "sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==",
+ "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
"dev": true,
- "requires": {
- "@types/react": "*"
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "@types/react-helmet": {
- "version": "5.0.15",
- "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-5.0.15.tgz",
- "integrity": "sha512-CCjqvecDJTXRrHG8aTc2YECcQCl26za/q+NaBRvy/wtm0Uh38koM2dpv2bG1xJV4ckz3t1lm2/5KU6nt2s9BWg==",
+ "node_modules/@csstools/postcss-color-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz",
+ "integrity": "sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA==",
"dev": true,
- "requires": {
- "@types/react": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/react-modal": {
- "version": "3.10.5",
- "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.10.5.tgz",
- "integrity": "sha512-iLL9afYbcgYlboW2J8mFNKH2tFgErIHR0q+JgHotKrFK99+d97v+V/dxL5LVoxt1OjlnWsuwj8sZuBMVsI1MtQ==",
+ "node_modules/@csstools/postcss-color-function-display-p3-linear": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz",
+ "integrity": "sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg==",
"dev": true,
- "requires": {
- "@types/react": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/react-select": {
- "version": "3.0.11",
- "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.11.tgz",
- "integrity": "sha512-ggUsAdZuRFtLMjGMcdf9SeeE678TRq3lAKj1fbwGM8JAZTIzCu1CED0dvJgFVCPT2bDs8TcBD6+6SN6i4e7JYQ==",
+ "node_modules/@csstools/postcss-color-mix-function": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz",
+ "integrity": "sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag==",
"dev": true,
- "requires": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "@types/react-transition-group": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/react-transition-group": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz",
- "integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==",
+ "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz",
+ "integrity": "sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ==",
"dev": true,
- "requires": {
- "@types/react": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/rimraf": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.3.tgz",
- "integrity": "sha512-dZfyfL/u9l/oi984hEXdmAjX3JHry7TLWw43u1HQ8HhPv6KtfxnrZ3T/bleJ0GEvnk9t5sM7eePkgMqz3yBcGg==",
+ "node_modules/@csstools/postcss-content-alt-text": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz",
+ "integrity": "sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q==",
"dev": true,
- "requires": {
- "@types/glob": "*",
- "@types/node": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/sinon": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.1.tgz",
- "integrity": "sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==",
- "dev": true
- },
- "@types/sinon-chai": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.3.tgz",
- "integrity": "sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==",
+ "node_modules/@csstools/postcss-contrast-color-function": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz",
+ "integrity": "sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA==",
"dev": true,
- "requires": {
- "@types/chai": "*",
- "@types/sinon": "*"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/sizzle": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
- "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
- "dev": true
- },
- "@types/styled-components": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-4.1.8.tgz",
- "integrity": "sha512-NrG0wmB9Rafy5i00GFxUM/uEge148bX2QPr+Q/MI2fXrew6WOp1hN2A3YEG0AeT45z47CMdJ3BEffPsdQCWayA==",
+ "node_modules/@csstools/postcss-exponential-functions": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz",
+ "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==",
"dev": true,
- "requires": {
- "@types/node": "*",
- "@types/react": "*",
- "csstype": "^2.2.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@types/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=",
- "dev": true
- },
- "@typescript-eslint/eslint-plugin": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.29.0.tgz",
- "integrity": "sha512-X/YAY7azKirENm4QRpT7OVmzok02cSkqeIcLmdz6gXUQG4Hk0Fi9oBAynSAyNXeGdMRuZvjBa0c1Lu0dn/u6VA==",
+ "node_modules/@csstools/postcss-font-format-keywords": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz",
+ "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==",
"dev": true,
- "requires": {
- "@typescript-eslint/experimental-utils": "2.29.0",
- "functional-red-black-tree": "^1.0.1",
- "regexpp": "^3.0.0",
- "tsutils": "^3.17.1"
- },
- "dependencies": {
- "regexpp": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
- "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@typescript-eslint/experimental-utils": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz",
- "integrity": "sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==",
+ "node_modules/@csstools/postcss-gamut-mapping": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz",
+ "integrity": "sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw==",
"dev": true,
- "requires": {
- "@types/json-schema": "^7.0.3",
- "@typescript-eslint/typescript-estree": "2.29.0",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^2.0.0"
- },
- "dependencies": {
- "eslint-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
- "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@typescript-eslint/parser": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.29.0.tgz",
- "integrity": "sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA==",
+ "node_modules/@csstools/postcss-gradients-interpolation-method": {
+ "version": "5.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz",
+ "integrity": "sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow==",
"dev": true,
- "requires": {
- "@types/eslint-visitor-keys": "^1.0.0",
- "@typescript-eslint/experimental-utils": "2.29.0",
- "@typescript-eslint/typescript-estree": "2.29.0",
- "eslint-visitor-keys": "^1.1.0"
- },
- "dependencies": {
- "@typescript-eslint/experimental-utils": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz",
- "integrity": "sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==",
- "dev": true,
- "requires": {
- "@types/json-schema": "^7.0.3",
- "@typescript-eslint/typescript-estree": "2.29.0",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^2.0.0"
- }
- },
- "@typescript-eslint/typescript-estree": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz",
- "integrity": "sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==",
- "dev": true,
- "requires": {
- "debug": "^4.1.1",
- "eslint-visitor-keys": "^1.1.0",
- "glob": "^7.1.6",
- "is-glob": "^4.0.1",
- "lodash": "^4.17.15",
- "semver": "^6.3.0",
- "tsutils": "^3.17.1"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "eslint-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
- "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@typescript-eslint/typescript-estree": {
- "version": "2.29.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz",
- "integrity": "sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==",
+ "node_modules/@csstools/postcss-hwb-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz",
+ "integrity": "sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA==",
"dev": true,
- "requires": {
- "debug": "^4.1.1",
- "eslint-visitor-keys": "^1.1.0",
- "glob": "^7.1.6",
- "is-glob": "^4.0.1",
- "lodash": "^4.17.15",
- "semver": "^6.3.0",
- "tsutils": "^3.17.1"
- },
- "dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/ast": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
- "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
+ "node_modules/@csstools/postcss-ic-unit": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz",
+ "integrity": "sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg==",
"dev": true,
- "requires": {
- "@webassemblyjs/helper-module-context": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/wast-parser": "1.8.5"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/floating-point-hex-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
- "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
- "dev": true
- },
- "@webassemblyjs/helper-api-error": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
- "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
- "dev": true
- },
- "@webassemblyjs/helper-buffer": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
- "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
- "dev": true
- },
- "@webassemblyjs/helper-code-frame": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
- "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
+ "node_modules/@csstools/postcss-initial": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz",
+ "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==",
"dev": true,
- "requires": {
- "@webassemblyjs/wast-printer": "1.8.5"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/helper-fsm": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
- "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
- "dev": true
- },
- "@webassemblyjs/helper-module-context": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
- "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
+ "node_modules/@csstools/postcss-is-pseudo-class": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz",
+ "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==",
"dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "mamacro": "^0.0.3"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
- "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
- "dev": true
- },
- "@webassemblyjs/helper-wasm-section": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
- "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
+ "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
"dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
}
},
- "@webassemblyjs/ieee754": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
- "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
+ "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
"dev": true,
- "requires": {
- "@xtuc/ieee754": "^1.2.0"
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "@webassemblyjs/leb128": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
- "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
+ "node_modules/@csstools/postcss-light-dark-function": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz",
+ "integrity": "sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA==",
"dev": true,
- "requires": {
- "@xtuc/long": "4.2.2"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/utf8": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
- "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
- "dev": true
- },
- "@webassemblyjs/wasm-edit": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
- "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/helper-wasm-section": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5",
- "@webassemblyjs/wasm-opt": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5",
- "@webassemblyjs/wast-printer": "1.8.5"
- }
- },
- "@webassemblyjs/wasm-gen": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
- "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/ieee754": "1.8.5",
- "@webassemblyjs/leb128": "1.8.5",
- "@webassemblyjs/utf8": "1.8.5"
- }
- },
- "@webassemblyjs/wasm-opt": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
- "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-buffer": "1.8.5",
- "@webassemblyjs/wasm-gen": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5"
- }
- },
- "@webassemblyjs/wasm-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
- "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-api-error": "1.8.5",
- "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
- "@webassemblyjs/ieee754": "1.8.5",
- "@webassemblyjs/leb128": "1.8.5",
- "@webassemblyjs/utf8": "1.8.5"
- }
- },
- "@webassemblyjs/wast-parser": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
- "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/floating-point-hex-parser": "1.8.5",
- "@webassemblyjs/helper-api-error": "1.8.5",
- "@webassemblyjs/helper-code-frame": "1.8.5",
- "@webassemblyjs/helper-fsm": "1.8.5",
- "@xtuc/long": "4.2.2"
+ "node_modules/@csstools/postcss-logical-float-and-clear": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz",
+ "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@webassemblyjs/wast-printer": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
- "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
+ "node_modules/@csstools/postcss-logical-overflow": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz",
+ "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==",
"dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/wast-parser": "1.8.5",
- "@xtuc/long": "4.2.2"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "@xtuc/ieee754": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
- "dev": true
- },
- "@xtuc/long": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
- "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
- "dev": true
- },
- "abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
- "dev": true
+ "node_modules/@csstools/postcss-logical-overscroll-behavior": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz",
+ "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "abort-controller": {
+ "node_modules/@csstools/postcss-logical-resize": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
- "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz",
+ "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==",
"dev": true,
- "requires": {
- "event-target-shim": "^5.0.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "accepts": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
- "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
- "requires": {
- "mime-types": "~2.1.18",
- "negotiator": "0.6.1"
- },
- "dependencies": {
- "mime-db": {
- "version": "1.37.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
- "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
+ "node_modules/@csstools/postcss-logical-viewport-units": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz",
+ "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "mime-types": {
- "version": "2.1.21",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
- "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
- "requires": {
- "mime-db": "~1.37.0"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "acorn": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
- "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
- "dev": true
- },
- "acorn-jsx": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
- "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
- "dev": true
- },
- "address": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz",
- "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
- "dev": true
- },
- "after": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
- "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
- },
- "agent-base": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
- "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "node_modules/@csstools/postcss-media-minmax": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz",
+ "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==",
"dev": true,
- "requires": {
- "es6-promisify": "^5.0.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "aggregate-error": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.0.tgz",
- "integrity": "sha512-yKD9kEoJIR+2IFqhMwayIBgheLYbB3PS2OBhWae1L/ODTd/JF/30cW0bc9TqzRL3k4U41Dieu3BF4I29p8xesA==",
+ "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz",
+ "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==",
"dev": true,
- "requires": {
- "clean-stack": "^2.0.0",
- "indent-string": "^3.2.0"
- }
- },
- "airbnb-prop-types": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz",
- "integrity": "sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA==",
- "dev": true,
- "requires": {
- "array.prototype.find": "^2.1.0",
- "function.prototype.name": "^1.1.1",
- "has": "^1.0.3",
- "is-regex": "^1.0.4",
- "object-is": "^1.0.1",
- "object.assign": "^4.1.0",
- "object.entries": "^1.1.0",
- "prop-types": "^15.7.2",
- "prop-types-exact": "^1.2.0",
- "react-is": "^16.9.0"
- },
- "dependencies": {
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "react-is": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
- "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "ajv": {
- "version": "6.10.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
- "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
- "requires": {
- "fast-deep-equal": "^2.0.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "dependencies": {
- "fast-deep-equal": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
- "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+ "node_modules/@csstools/postcss-nested-calc": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz",
+ "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "ajv-errors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
- "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
- "dev": true
- },
- "ajv-keywords": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
- "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
- "dev": true
+ "node_modules/@csstools/postcss-normalize-display-values": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz",
+ "integrity": "sha512-TQUGBuRvxdc7TgNSTevYqrL8oItxiwPDixk20qCB5me/W8uF7BPbhRrAvFuhEoywQp/woRsUZ6SJ+sU5idZAIA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "alphanum-sort": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
- "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
- "dev": true
+ "node_modules/@csstools/postcss-oklab-function": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz",
+ "integrity": "sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "anafanafo": {
+ "node_modules/@csstools/postcss-position-area-property": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-1.0.0.tgz",
- "integrity": "sha512-pDPbI7SFRHu0byMXHAf+v74+LCcHSxnLYkcbfiV91XRlE+NSLpFCpEQdVUy9ZxZw/LuhTrOin4r8wlR3OFrKBA==",
- "requires": {
- "char-width-table-consumer": "^1.0.0"
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz",
+ "integrity": "sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "ansi-align": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
- "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==",
+ "node_modules/@csstools/postcss-progressive-custom-properties": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz",
+ "integrity": "sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw==",
"dev": true,
- "requires": {
- "string-width": "^3.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "ansi-colors": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
- "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
- "dev": true
- },
- "ansi-escapes": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
- "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
- "dev": true
- },
- "ansi-html": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
- "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
- "dev": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
+ "node_modules/@csstools/postcss-property-rule-prelude-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-property-rule-prelude-list/-/postcss-property-rule-prelude-list-1.0.0.tgz",
+ "integrity": "sha512-IxuQjUXq19fobgmSSvUDO7fVwijDJaZMvWQugxfEUxmjBeDCVaDuMpsZ31MsTm5xbnhA+ElDi0+rQ7sQQGisFA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "any-observable": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
- "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
- "dev": true
+ "node_modules/@csstools/postcss-random-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz",
+ "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "node_modules/@csstools/postcss-relative-color-syntax": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz",
+ "integrity": "sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw==",
"dev": true,
- "requires": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "append-transform": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
- "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
+ "node_modules/@csstools/postcss-scope-pseudo-class": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz",
+ "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==",
"dev": true,
- "requires": {
- "default-require-extensions": "^3.0.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "application-config-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.0.tgz",
- "integrity": "sha1-GTxfCoZUGkxm+6Hi3DhYM2LqXo8=",
- "dev": true
- },
- "aproba": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
- "dev": true
- },
- "arch": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz",
- "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==",
- "dev": true
- },
- "archy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
- "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
- "dev": true
- },
- "arg": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz",
- "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==",
- "dev": true
- },
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "requires": {
- "sprintf-js": "~1.0.2"
- }
- },
- "aria-query": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
- "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+ "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
"dev": true,
- "requires": {
- "ast-types-flow": "0.0.7",
- "commander": "^2.11.0"
- }
- },
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
- "dev": true
- },
- "arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "dev": true
- },
- "arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
- "dev": true
- },
- "array-filter": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
- "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
- "dev": true
- },
- "array-find-index": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
- "dev": true
- },
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
- "dev": true
- },
- "array-includes": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
- "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
- "requires": {
- "define-properties": "^1.1.2",
- "es-abstract": "^1.7.0"
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "array-map": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
- "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
- "dev": true
- },
- "array-reduce": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
- "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
- "dev": true
- },
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
- "array-uniq": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
- "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
- "dev": true
- },
- "array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
- "dev": true
- },
- "array.prototype.find": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.0.tgz",
- "integrity": "sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==",
+ "node_modules/@csstools/postcss-sign-functions": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz",
+ "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.13.0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
- "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "array.prototype.flat": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
- "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+ "node_modules/@csstools/postcss-stepped-value-functions": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz",
+ "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
- "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
- "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimstart": "^1.0.0"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
- "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimend": "^1.0.0"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "arraybuffer.slice": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
- "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
- },
- "arrify": {
+ "node_modules/@csstools/postcss-syntax-descriptor-syntax-production": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
- "dev": true
- },
- "asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
- },
- "asn1": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
- "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
- "requires": {
- "safer-buffer": "~2.1.0"
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-syntax-descriptor-syntax-production/-/postcss-syntax-descriptor-syntax-production-1.0.1.tgz",
+ "integrity": "sha512-GneqQWefjM//f4hJ/Kbox0C6f2T7+pi4/fqTqOFGTL3EjnvOReTqO1qUQ30CaUjkwjYq9qZ41hzarrAxCc4gow==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "asn1.js": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
- "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "node_modules/@csstools/postcss-system-ui-font-family": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz",
+ "integrity": "sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ==",
"dev": true,
- "requires": {
- "bn.js": "^4.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "assert": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
- "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+ "node_modules/@csstools/postcss-text-decoration-shorthand": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz",
+ "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==",
"dev": true,
- "requires": {
- "object-assign": "^4.1.1",
- "util": "0.10.3"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
- "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "util": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
- "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
- "dev": true,
- "requires": {
- "inherits": "2.0.1"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
- },
- "assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
- },
- "assign-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
- "dev": true
- },
- "ast-types-flow": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
- "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
- "dev": true
- },
- "astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
- "dev": true
- },
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
- "dev": true
- },
- "async-each": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
- "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
- "dev": true
- },
- "async-limiter": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
- "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
- },
- "async-retry": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz",
- "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==",
+ "node_modules/@csstools/postcss-trigonometric-functions": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz",
+ "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==",
"dev": true,
- "requires": {
- "retry": "0.12.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
- },
- "atob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz",
- "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=",
- "dev": true
- },
- "atob-lite": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
- "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=",
- "dev": true
- },
- "auto-bind": {
+ "node_modules/@csstools/postcss-unset-value": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz",
- "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz",
+ "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==",
"dev": true,
- "optional": true
- },
- "autoprefixer": {
- "version": "9.7.4",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz",
- "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==",
- "dev": true,
- "requires": {
- "browserslist": "^4.8.3",
- "caniuse-lite": "^1.0.30001020",
- "chalk": "^2.4.2",
- "normalize-range": "^0.1.2",
- "num2fraction": "^1.2.2",
- "postcss": "^7.0.26",
- "postcss-value-parser": "^4.0.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/@csstools/utilities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz",
+ "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "aws-sign2": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ "node_modules/@cypress/request": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@cypress/request/-/request-4.0.1.tgz",
+ "integrity": "sha512-y20e+e6dFYkOUUJLVUZTsJRuTiXZaUQ32WD+R/ux/HBybbTx4ge7cNINcua0pU8+SNkKuRbOF12mBmzuzM8n5w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~4.0.4",
+ "http-signature": "~1.4.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "performance-now": "^2.1.0",
+ "qs": "^6.15.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "^5.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "engines": {
+ "node": ">= 14.17.0"
+ }
},
- "aws4": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
+ "node_modules/@cypress/request/node_modules/tough-cookie": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
},
- "axios": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
- "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+ "node_modules/@cypress/xvfb": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
+ "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
"dev": true,
- "requires": {
- "follow-redirects": "1.5.10"
+ "dependencies": {
+ "debug": "^3.1.0",
+ "lodash.once": "^4.1.1"
}
},
- "axobject-query": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz",
- "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==",
- "dev": true
+ "node_modules/@cypress/xvfb/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
},
- "babel-code-frame": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
"dev": true,
- "requires": {
- "chalk": "^1.1.3",
- "esutils": "^2.0.2",
- "js-tokens": "^3.0.2"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@docsearch/core": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.3.tgz",
+ "integrity": "sha512-rUOujwIpxJRgD7+kicVsI3D5sqBvdiRTquzWBpTEXZs8ZXfGbfzpus5HqumaNYTppN2HvH8E2yNuRwYdHJeOlA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": ">= 16.8.0 < 20.0.0",
+ "react": ">= 16.8.0 < 20.0.0",
+ "react-dom": ">= 16.8.0 < 20.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
},
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ "react": {
+ "optional": true
},
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
+ "react-dom": {
+ "optional": true
}
}
},
- "babel-core": {
- "version": "7.0.0-bridge.0",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
- "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
- "dev": true
+ "node_modules/@docsearch/css": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.3.tgz",
+ "integrity": "sha512-nlOwcXcsNAptQl4vlL4MA78qNJKO0Qlds5GuBjCoePgkebTXLSf8Qt1oyZ3YBshYupKXG9VRGEsk1zr23d+bzQ==",
+ "dev": true,
+ "license": "MIT"
},
- "babel-eslint": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
- "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==",
+ "node_modules/@docsearch/react": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.3.tgz",
+ "integrity": "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g==",
"dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@babel/parser": "^7.7.0",
- "@babel/traverse": "^7.7.0",
- "@babel/types": "^7.7.0",
- "eslint-visitor-keys": "^1.0.0",
- "resolve": "^1.12.0"
- },
- "dependencies": {
- "@babel/parser": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz",
- "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==",
- "dev": true
- },
- "@babel/types": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz",
- "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-core": "1.19.2",
+ "@docsearch/core": "4.6.3",
+ "@docsearch/css": "4.6.3"
+ },
+ "peerDependencies": {
+ "@types/react": ">= 16.8.0 < 20.0.0",
+ "react": ">= 16.8.0 < 20.0.0",
+ "react-dom": ">= 16.8.0 < 20.0.0",
+ "search-insights": ">= 1 < 3"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
},
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
+ "react": {
+ "optional": true
},
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- }
- }
- },
- "babel-loader": {
- "version": "8.0.6",
- "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
- "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==",
- "dev": true,
- "requires": {
- "find-cache-dir": "^2.0.0",
- "loader-utils": "^1.0.2",
- "mkdirp": "^0.5.1",
- "pify": "^4.0.1"
- },
- "dependencies": {
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "dev": true
- }
- }
- },
- "babel-plugin-add-module-exports": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.3.3.tgz",
- "integrity": "sha512-hC37mm7aAdEb1n8SgggG8a1QuhZapsY/XLCi4ETSH6AVjXBCWEa50CXlOsAMPPWLnSx5Ns6mzz39uvuseh0Xjg==",
- "dev": true,
- "requires": {
- "chokidar": "^2.0.4"
- },
- "dependencies": {
- "chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "dev": true,
- "optional": true,
- "requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "fsevents": "^1.2.7",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- }
+ "react-dom": {
+ "optional": true
},
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
+ "search-insights": {
"optional": true
}
}
},
- "babel-plugin-dynamic-import-node": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz",
- "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==",
+ "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-core": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz",
+ "integrity": "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==",
"dev": true,
- "requires": {
- "object.assign": "^4.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-plugin-algolia-insights": "1.19.2",
+ "@algolia/autocomplete-shared": "1.19.2"
}
},
- "babel-plugin-emotion": {
- "version": "10.0.29",
- "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.29.tgz",
- "integrity": "sha512-7Jpi1OCxjyz0k163lKtqP+LHMg5z3S6A7vMBfHnF06l2unmtsOmFDzZBpGf0CWo1G4m8UACfVcDJiSiRuu/cSw==",
+ "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-plugin-algolia-insights": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz",
+ "integrity": "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==",
"dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.0.0",
- "@emotion/hash": "0.8.0",
- "@emotion/memoize": "0.7.4",
- "@emotion/serialize": "^0.11.16",
- "babel-plugin-macros": "^2.0.0",
- "babel-plugin-syntax-jsx": "^6.18.0",
- "convert-source-map": "^1.5.0",
- "escape-string-regexp": "^1.0.5",
- "find-root": "^1.1.0",
- "source-map": "^0.5.7"
- },
+ "license": "MIT",
"dependencies": {
- "@emotion/memoize": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
- "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "@algolia/autocomplete-shared": "1.19.2"
+ },
+ "peerDependencies": {
+ "search-insights": ">= 1 < 3"
}
},
- "babel-plugin-inline-react-svg": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-inline-react-svg/-/babel-plugin-inline-react-svg-1.1.1.tgz",
- "integrity": "sha512-KCCzSKJUigDXd/dxJDE6uNyVTYE46FiTt8Md3vpYHtbADeTjOLJq5LkmaVpISplxKCK25VZU8sha2Km6uIEFJA==",
+ "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-shared": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz",
+ "integrity": "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==",
"dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/parser": "^7.0.0",
- "lodash.isplainobject": "^4.0.6",
- "resolve": "^1.10.0",
- "svgo": "^0.7.2"
- },
+ "license": "MIT",
+ "peerDependencies": {
+ "@algolia/client-search": ">= 4.9.1 < 6",
+ "algoliasearch": ">= 4.9.1 < 6"
+ }
+ },
+ "node_modules/@docusaurus/babel": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.10.2.tgz",
+ "integrity": "sha512-aJ1hpGyvfkte3dDAfNbWM4biW4yWZBVz7TIGLZP+v+tWOBgxX3e0N5ZIXHIvmfNNXTI77pcHUx3KmtOk05Ze3Q==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "coa": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
- "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
- "dev": true,
- "requires": {
- "q": "^1.1.2"
- }
- },
- "csso": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
- "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
- "dev": true,
- "requires": {
- "clap": "^1.0.9",
- "source-map": "^0.5.3"
- }
- },
- "esprima": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
- "dev": true
- },
- "js-yaml": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
- "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
- "dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^2.6.0"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
- "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "svgo": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
- "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=",
- "dev": true,
- "requires": {
- "coa": "~1.0.1",
- "colors": "~1.1.2",
- "csso": "~2.3.1",
- "js-yaml": "~3.7.0",
- "mkdirp": "~0.5.1",
- "sax": "~1.2.1",
- "whet.extend": "~0.9.9"
- },
- "dependencies": {
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- }
- }
- }
- }
- },
- "babel-plugin-istanbul": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz",
- "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^4.0.0",
- "test-exclude": "^6.0.0"
- },
- "dependencies": {
- "@babel/generator": {
- "version": "7.7.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz",
- "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.7.4",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.13",
- "source-map": "^0.5.0"
- }
- },
- "@babel/helper-function-name": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz",
- "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==",
- "dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.7.4",
- "@babel/template": "^7.7.4",
- "@babel/types": "^7.7.4"
- }
- },
- "@babel/helper-get-function-arity": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz",
- "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.7.4"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz",
- "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.7.4"
- }
- },
- "@babel/parser": {
- "version": "7.7.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz",
- "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==",
- "dev": true
- },
- "@babel/template": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz",
- "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@babel/parser": "^7.7.4",
- "@babel/types": "^7.7.4"
- }
- },
- "@babel/traverse": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz",
- "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.5.5",
- "@babel/generator": "^7.7.4",
- "@babel/helper-function-name": "^7.7.4",
- "@babel/helper-split-export-declaration": "^7.7.4",
- "@babel/parser": "^7.7.4",
- "@babel/types": "^7.7.4",
- "debug": "^4.1.0",
- "globals": "^11.1.0",
- "lodash": "^4.17.13"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.5.5",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
- "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.0.0"
- }
- }
- }
- },
- "@babel/types": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
- "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "istanbul-lib-coverage": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
- "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
- "dev": true
- },
- "istanbul-lib-instrument": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz",
- "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/template": "^7.7.4",
- "@babel/traverse": "^7.7.4",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- }
+ "@babel/core": "^7.25.9",
+ "@babel/generator": "^7.25.9",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-transform-runtime": "^7.25.9",
+ "@babel/preset-env": "^7.25.9",
+ "@babel/preset-react": "^7.25.9",
+ "@babel/preset-typescript": "^7.25.9",
+ "@babel/runtime": "^7.25.9",
+ "@babel/traverse": "^7.25.9",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "babel-plugin-dynamic-import-node": "^2.3.3",
+ "fs-extra": "^11.1.1",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ }
+ },
+ "node_modules/@docusaurus/bundler": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.10.2.tgz",
+ "integrity": "sha512-i0ZNcy0f0WhaOlYVgzLsWhIoEXO9kS3HRoKPtgE6vQtZUq7arKZaYdNBudr3mqCmd+TyOkwtwfHgs1ENj07r5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.25.9",
+ "@docusaurus/babel": "3.10.2",
+ "@docusaurus/cssnano-preset": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "babel-loader": "^9.2.1",
+ "clean-css": "^5.3.3",
+ "copy-webpack-plugin": "^11.0.0",
+ "css-loader": "^6.11.0",
+ "css-minimizer-webpack-plugin": "^5.0.1",
+ "cssnano": "^6.1.2",
+ "file-loader": "^6.2.0",
+ "html-minifier-terser": "^7.2.0",
+ "mini-css-extract-plugin": "^2.9.2",
+ "null-loader": "^4.0.1",
+ "postcss": "^8.5.4",
+ "postcss-loader": "^7.3.4",
+ "postcss-preset-env": "^10.2.1",
+ "terser-webpack-plugin": "^5.3.9",
+ "tslib": "^2.6.0",
+ "url-loader": "^4.1.1",
+ "webpack": "^5.95.0",
+ "webpackbar": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "@docusaurus/faster": "*"
+ },
+ "peerDependenciesMeta": {
+ "@docusaurus/faster": {
+ "optional": true
}
}
},
- "babel-plugin-macros": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
- "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.7.2",
- "cosmiconfig": "^6.0.0",
- "resolve": "^1.12.0"
- },
- "dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "dev": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- }
- },
- "import-fresh": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
- "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
- "dev": true,
- "requires": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- }
- },
- "parse-json": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
- "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1",
- "lines-and-columns": "^1.1.6"
- }
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
+ "node_modules/@docusaurus/core": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.10.2.tgz",
+ "integrity": "sha512-EYByj6nk+aD9KeVxV6Hmo2/nAAT79P21Y82ycTBOBtrmqilloIbIEhgL2/8Xpt2Jz/pgNqHAwyusOGwmbKeJmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/babel": "3.10.2",
+ "@docusaurus/bundler": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "boxen": "^6.2.1",
+ "chalk": "^4.1.2",
+ "chokidar": "^3.5.3",
+ "cli-table3": "^0.6.3",
+ "combine-promises": "^1.1.0",
+ "commander": "^5.1.0",
+ "core-js": "^3.31.1",
+ "detect-port": "^2.1.0",
+ "escape-html": "^1.0.3",
+ "eta": "^2.2.0",
+ "eval": "^0.1.8",
+ "execa": "^5.1.1",
+ "fs-extra": "^11.1.1",
+ "html-tags": "^3.3.1",
+ "html-webpack-plugin": "^5.6.0",
+ "leven": "^3.1.0",
+ "lodash": "^4.17.21",
+ "open": "^8.4.0",
+ "p-map": "^4.0.0",
+ "prompts": "^2.4.2",
+ "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0",
+ "react-loadable": "npm:@docusaurus/react-loadable@6.0.0",
+ "react-loadable-ssr-addon-v5-slorber": "^1.0.3",
+ "react-router": "^5.3.4",
+ "react-router-config": "^5.1.1",
+ "react-router-dom": "^5.3.4",
+ "semver": "^7.5.4",
+ "serve-handler": "^6.1.7",
+ "tinypool": "^1.0.2",
+ "tslib": "^2.6.0",
+ "update-notifier": "^6.0.2",
+ "webpack": "^5.95.0",
+ "webpack-bundle-analyzer": "^4.10.2",
+ "webpack-dev-server": "^5.2.2",
+ "webpack-merge": "^6.0.1"
+ },
+ "bin": {
+ "docusaurus": "bin/docusaurus.mjs"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "@docusaurus/faster": "*",
+ "@mdx-js/react": "^3.0.0",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@docusaurus/faster": {
+ "optional": true
}
}
},
- "babel-plugin-remove-graphql-queries": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.8.1.tgz",
- "integrity": "sha512-c/JNri17WypqZNnMsX2PweMe8e5hsJcYNO/VnUBX9iUIvmKBjd143RaUQq0xYa6bpQF0kzpTFVR0sOp+cQlBOQ==",
- "dev": true
- },
- "babel-plugin-styled-components": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz",
- "integrity": "sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg==",
+ "node_modules/@docusaurus/core/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.0.0",
- "@babel/helper-module-imports": "^7.0.0",
- "babel-plugin-syntax-jsx": "^6.18.0",
- "lodash": "^4.17.11"
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "babel-plugin-syntax-jsx": {
- "version": "6.18.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
- "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
- "dev": true
- },
- "babel-plugin-transform-react-remove-prop-types": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
- "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==",
- "dev": true
- },
- "babel-preset-gatsby": {
- "version": "0.2.36",
- "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-0.2.36.tgz",
- "integrity": "sha512-vmqN6ht4B28dHlK7Qsau3JseHwTEkLjf2QkUcUKlYCuVk7skZkbN2B6O8QeJQTQ30V/6uUKiNMU/U0nc0RYMNQ==",
+ "node_modules/@docusaurus/core/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
- "requires": {
- "@babel/plugin-proposal-class-properties": "^7.8.3",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-proposal-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-transform-runtime": "^7.8.3",
- "@babel/plugin-transform-spread": "^7.8.3",
- "@babel/preset-env": "^7.8.7",
- "@babel/preset-react": "^7.8.3",
- "@babel/runtime": "^7.8.7",
- "babel-plugin-dynamic-import-node": "^2.3.0",
- "babel-plugin-macros": "^2.8.0",
- "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
- "gatsby-core-utils": "^1.0.34"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/compat-data": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz",
- "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==",
- "dev": true,
- "requires": {
- "browserslist": "^4.9.1",
- "invariant": "^2.2.4",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- }
- }
- },
- "@babel/helper-builder-react-jsx": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz",
- "integrity": "sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw==",
- "dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/types": "^7.9.0"
- }
- },
- "@babel/helper-compilation-targets": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz",
- "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.8.6",
- "browserslist": "^4.9.1",
- "invariant": "^2.2.4",
- "levenary": "^1.1.1",
- "semver": "^5.5.0"
- },
- "dependencies": {
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- }
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
- "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.8.3"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
- "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-simple-access": "^7.8.3",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/template": "^7.8.6",
- "@babel/types": "^7.9.0",
- "lodash": "^4.17.13"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/highlight": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
- "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
- }
- },
- "@babel/plugin-proposal-object-rest-spread": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz",
- "integrity": "sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0"
- }
- },
- "@babel/plugin-proposal-optional-chaining": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz",
- "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-jsx": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz",
- "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-transform-classes": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz",
- "integrity": "sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==",
- "dev": true,
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.8.3",
- "@babel/helper-define-map": "^7.8.3",
- "@babel/helper-function-name": "^7.8.3",
- "@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.6",
- "@babel/helper-split-export-declaration": "^7.8.3",
- "globals": "^11.1.0"
- }
- },
- "@babel/plugin-transform-for-of": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz",
- "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-modules-amd": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz",
- "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==",
- "dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- }
- },
- "@babel/plugin-transform-modules-commonjs": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz",
- "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==",
- "dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-simple-access": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- }
- },
- "@babel/plugin-transform-modules-systemjs": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz",
- "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==",
- "dev": true,
- "requires": {
- "@babel/helper-hoist-variables": "^7.8.3",
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "babel-plugin-dynamic-import-node": "^2.3.0"
- }
- },
- "@babel/plugin-transform-modules-umd": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz",
- "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==",
- "dev": true,
- "requires": {
- "@babel/helper-module-transforms": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-parameters": {
- "version": "7.9.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz",
- "integrity": "sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==",
- "dev": true,
- "requires": {
- "@babel/helper-get-function-arity": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-react-display-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz",
- "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-transform-react-jsx": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.4.tgz",
- "integrity": "sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw==",
- "dev": true,
- "requires": {
- "@babel/helper-builder-react-jsx": "^7.9.0",
- "@babel/helper-builder-react-jsx-experimental": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-jsx": "^7.8.3"
- }
- },
- "@babel/plugin-transform-react-jsx-self": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz",
- "integrity": "sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-jsx": "^7.8.3"
- }
- },
- "@babel/plugin-transform-react-jsx-source": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz",
- "integrity": "sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-jsx": "^7.8.3"
- }
- },
- "@babel/plugin-transform-regenerator": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz",
- "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==",
- "dev": true,
- "requires": {
- "regenerator-transform": "^0.14.2"
- }
- },
- "@babel/plugin-transform-runtime": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz",
- "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "resolve": "^1.8.1",
- "semver": "^5.5.1"
- }
- },
- "@babel/plugin-transform-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz",
- "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/preset-env": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz",
- "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.9.0",
- "@babel/helper-compilation-targets": "^7.8.7",
- "@babel/helper-module-imports": "^7.8.3",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-proposal-async-generator-functions": "^7.8.3",
- "@babel/plugin-proposal-dynamic-import": "^7.8.3",
- "@babel/plugin-proposal-json-strings": "^7.8.3",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-proposal-numeric-separator": "^7.8.3",
- "@babel/plugin-proposal-object-rest-spread": "^7.9.0",
- "@babel/plugin-proposal-optional-catch-binding": "^7.8.3",
- "@babel/plugin-proposal-optional-chaining": "^7.9.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
- "@babel/plugin-syntax-async-generators": "^7.8.0",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0",
- "@babel/plugin-syntax-json-strings": "^7.8.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
- "@babel/plugin-syntax-numeric-separator": "^7.8.0",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0",
- "@babel/plugin-syntax-top-level-await": "^7.8.3",
- "@babel/plugin-transform-arrow-functions": "^7.8.3",
- "@babel/plugin-transform-async-to-generator": "^7.8.3",
- "@babel/plugin-transform-block-scoped-functions": "^7.8.3",
- "@babel/plugin-transform-block-scoping": "^7.8.3",
- "@babel/plugin-transform-classes": "^7.9.0",
- "@babel/plugin-transform-computed-properties": "^7.8.3",
- "@babel/plugin-transform-destructuring": "^7.8.3",
- "@babel/plugin-transform-dotall-regex": "^7.8.3",
- "@babel/plugin-transform-duplicate-keys": "^7.8.3",
- "@babel/plugin-transform-exponentiation-operator": "^7.8.3",
- "@babel/plugin-transform-for-of": "^7.9.0",
- "@babel/plugin-transform-function-name": "^7.8.3",
- "@babel/plugin-transform-literals": "^7.8.3",
- "@babel/plugin-transform-member-expression-literals": "^7.8.3",
- "@babel/plugin-transform-modules-amd": "^7.9.0",
- "@babel/plugin-transform-modules-commonjs": "^7.9.0",
- "@babel/plugin-transform-modules-systemjs": "^7.9.0",
- "@babel/plugin-transform-modules-umd": "^7.9.0",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3",
- "@babel/plugin-transform-new-target": "^7.8.3",
- "@babel/plugin-transform-object-super": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.8.7",
- "@babel/plugin-transform-property-literals": "^7.8.3",
- "@babel/plugin-transform-regenerator": "^7.8.7",
- "@babel/plugin-transform-reserved-words": "^7.8.3",
- "@babel/plugin-transform-shorthand-properties": "^7.8.3",
- "@babel/plugin-transform-spread": "^7.8.3",
- "@babel/plugin-transform-sticky-regex": "^7.8.3",
- "@babel/plugin-transform-template-literals": "^7.8.3",
- "@babel/plugin-transform-typeof-symbol": "^7.8.4",
- "@babel/plugin-transform-unicode-regex": "^7.8.3",
- "@babel/preset-modules": "^0.1.3",
- "@babel/types": "^7.9.0",
- "browserslist": "^4.9.1",
- "core-js-compat": "^3.6.2",
- "invariant": "^2.2.2",
- "levenary": "^1.1.1",
- "semver": "^5.5.0"
- }
- },
- "@babel/preset-react": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.4.tgz",
- "integrity": "sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-transform-react-display-name": "^7.8.3",
- "@babel/plugin-transform-react-jsx": "^7.9.4",
- "@babel/plugin-transform-react-jsx-development": "^7.9.0",
- "@babel/plugin-transform-react-jsx-self": "^7.9.0",
- "@babel/plugin-transform-react-jsx-source": "^7.9.0"
- }
- },
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "@babel/template": {
- "version": "7.8.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
- "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.6",
- "@babel/types": "^7.8.6"
- }
- },
- "@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
- "lodash": "^4.17.13",
- "to-fast-properties": "^2.0.0"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "regenerator-transform": {
- "version": "0.14.4",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz",
- "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.8.4",
- "private": "^0.1.8"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "babel-runtime": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "node_modules/@docusaurus/core/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
- "requires": {
- "core-js": "^2.4.0",
- "regenerator-runtime": "^0.11.0"
- },
+ "license": "MIT",
"dependencies": {
- "regenerator-runtime": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
- "dev": true
- }
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "backo2": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
- "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+ "node_modules/@docusaurus/core/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
},
- "badge-maker": {
- "version": "file:badge-maker",
- "requires": {
- "anafanafo": "^1.0.0",
- "is-css-color": "^1.0.0"
+ "node_modules/@docusaurus/core/node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
}
},
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
- },
- "base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "dev": true,
- "requires": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "dependencies": {
- "component-emitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
- "dev": true
- },
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- }
+ "node_modules/@docusaurus/core/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "base64-arraybuffer": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
- "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
- },
- "base64-js": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
- "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
- "dev": true
- },
- "batch": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
- "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
- "dev": true
- },
- "bcrypt-pbkdf": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
- "requires": {
- "tweetnacl": "^0.14.3"
+ "node_modules/@docusaurus/core/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "before-after-hook": {
+ "node_modules/@docusaurus/core/node_modules/human-signals": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz",
- "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==",
- "dev": true
- },
- "better-assert": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
- "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
- "requires": {
- "callsite": "1.0.0"
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
}
},
- "better-opn": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-1.0.0.tgz",
- "integrity": "sha512-q3eO2se4sFbTERB1dFBDdjTiIIpRohMErpwBX21lhPvmgmQNNrcQj0zbWRhMREDesJvyod9kxBS3kOtdAvkB/A==",
+ "node_modules/@docusaurus/core/node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
- "requires": {
- "open": "^6.4.0"
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
},
- "dependencies": {
- "open": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz",
- "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==",
- "dev": true,
- "requires": {
- "is-wsl": "^1.1.0"
- }
- }
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "better-queue": {
- "version": "3.8.10",
- "resolved": "https://registry.npmjs.org/better-queue/-/better-queue-3.8.10.tgz",
- "integrity": "sha512-e3gwNZgDCnNWl0An0Tz6sUjKDV9m6aB+K9Xg//vYeo8+KiH8pWhLFxkawcXhm6FpM//GfD9IQv/kmvWCAVVpKA==",
+ "node_modules/@docusaurus/core/node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
- "requires": {
- "better-queue-memory": "^1.0.1",
- "node-eta": "^0.9.0",
- "uuid": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "better-queue-memory": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/better-queue-memory/-/better-queue-memory-1.0.4.tgz",
- "integrity": "sha512-SWg5wFIShYffEmJpI6LgbL8/3Dqhku7xI1oEiy6FroP9DbcZlG0ZDjxvPdP9t7hTGW40IpIcC6zVoGT1oxjOuA==",
- "dev": true
- },
- "big.js": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
- "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
- "dev": true
- },
- "binary-extensions": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
- "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
- "dev": true
- },
- "binary-search": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
- "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
- },
- "bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "node_modules/@docusaurus/core/node_modules/open": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"dev": true,
- "optional": true,
- "requires": {
- "file-uri-to-path": "1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "bintrees": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
- "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ="
- },
- "blob": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
- "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
- },
- "bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
- "dev": true
- },
- "bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
- "dev": true
- },
- "body-parser": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
- "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "node_modules/@docusaurus/core/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "requires": {
- "bytes": "3.1.0",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "http-errors": "1.7.2",
- "iconv-lite": "0.4.24",
- "on-finished": "~2.3.0",
- "qs": "6.7.0",
- "raw-body": "2.4.0",
- "type-is": "~1.6.17"
- },
+ "license": "MIT",
"dependencies": {
- "qs": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
- "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
- "dev": true
- }
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "bonjour": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
- "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+ "node_modules/@docusaurus/cssnano-preset": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.2.tgz",
+ "integrity": "sha512-4gCnHRbJLTloiwfvFAa92tgb2gI4KYhvjfQVYnEaiMO/EgvWfCo1LwytHXen+1oZAN0VAlS0JAPxp3MsvKDa3A==",
"dev": true,
- "requires": {
- "array-flatten": "^2.1.0",
- "deep-equal": "^1.0.1",
- "dns-equal": "^1.0.0",
- "dns-txt": "^2.0.2",
- "multicast-dns": "^6.0.1",
- "multicast-dns-service-types": "^1.1.0"
- },
+ "license": "MIT",
"dependencies": {
- "array-flatten": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
- "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
- "dev": true
- }
+ "cssnano-preset-advanced": "^6.1.2",
+ "postcss": "^8.5.4",
+ "postcss-sort-media-queries": "^5.2.0",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
}
},
- "boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
- "dev": true
- },
- "boxen": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz",
- "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==",
+ "node_modules/@docusaurus/logger": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.10.2.tgz",
+ "integrity": "sha512-gSEwqtPfCAnC3ZSJY6xL7tcIfgg0vFD39jbv93eakuweyvO2864xR0K+kmKwBhkTCtWRNjuGGnb5rdmkD/ndqw==",
"dev": true,
- "requires": {
- "ansi-align": "^3.0.0",
- "camelcase": "^5.3.1",
- "chalk": "^3.0.0",
- "cli-boxes": "^2.2.0",
- "string-width": "^4.1.0",
- "term-size": "^2.1.0",
- "type-fest": "^0.8.1",
- "widest-line": "^3.1.0"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
- "term-size": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz",
- "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==",
- "dev": true
- },
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- },
- "widest-line": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
- "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
- "dev": true,
- "requires": {
- "string-width": "^4.0.0"
- }
- }
+ "chalk": "^4.1.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
}
},
- "brace-expansion": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
- "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "node_modules/@docusaurus/logger/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "node_modules/@docusaurus/logger/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "brorand": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
- "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
- "dev": true
- },
- "browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true
- },
- "browserify-aes": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
- "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
- "dev": true,
- "requires": {
- "buffer-xor": "^1.0.3",
- "cipher-base": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.3",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "node_modules/@docusaurus/logger/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "browserify-cipher": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
- "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "node_modules/@docusaurus/logger/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@docusaurus/logger/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@docusaurus/mdx-loader": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.10.2.tgz",
+ "integrity": "sha512-9Fd4V/SFjfrVQ0JH5EN0+iPWyFunvTeQE3gfyFeetqPaXMP0OylIjOw16dCuXG4NZJrYdBqwzjh18/h3gRi47w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "@mdx-js/mdx": "^3.0.0",
+ "@slorber/remark-comment": "^1.0.0",
+ "escape-html": "^1.0.3",
+ "estree-util-value-to-estree": "^3.0.1",
+ "file-loader": "^6.2.0",
+ "fs-extra": "^11.1.1",
+ "image-size": "^2.0.2",
+ "mdast-util-mdx": "^3.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "rehype-raw": "^7.0.0",
+ "remark-directive": "^3.0.0",
+ "remark-emoji": "^4.0.0",
+ "remark-frontmatter": "^5.0.0",
+ "remark-gfm": "^4.0.0",
+ "stringify-object": "^3.3.0",
+ "tslib": "^2.6.0",
+ "unified": "^11.0.3",
+ "unist-util-visit": "^5.0.0",
+ "url-loader": "^4.1.1",
+ "vfile": "^6.0.1",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/module-type-aliases": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.2.tgz",
+ "integrity": "sha512-h/I5e4jaAhDHW4vaLENi1i2hnOEnXY1t9R+nnRTbgUl7ymVRzN/HF7dDfj8rKYGj8gfIge+Ef+iYRAMtbGvsrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/types": "3.10.2",
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router-config": "*",
+ "@types/react-router-dom": "*",
+ "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0",
+ "react-loadable": "npm:@docusaurus/react-loadable@6.0.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@docusaurus/plugin-content-blog": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.2.tgz",
+ "integrity": "sha512-0cbEnNKf0InmLkhj/+nVRmqEnWEoOE8Mh+2x1qOXI0qYpCnphq4RXknVJ8BvybKRXqYVvbmdMfiJSup+k4tm5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/theme-common": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "cheerio": "1.0.0-rc.12",
+ "combine-promises": "^1.1.0",
+ "feed": "^4.2.2",
+ "fs-extra": "^11.1.1",
+ "lodash": "^4.17.21",
+ "schema-dts": "^1.1.2",
+ "srcset": "^4.0.0",
+ "tslib": "^2.6.0",
+ "unist-util-visit": "^5.0.0",
+ "utility-types": "^3.10.0",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "@docusaurus/plugin-content-docs": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/plugin-content-docs": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.2.tgz",
+ "integrity": "sha512-Sqwl4FPoZBDrlY8I2VU2H8O0M91CHp9T8ToMSkTZmjvHCif+1laqfXi6sTk8IfyVS/trN5yNjcWd1bFsGB6W5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/module-type-aliases": "3.10.2",
+ "@docusaurus/theme-common": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "@types/react-router-config": "^5.0.7",
+ "combine-promises": "^1.1.0",
+ "fs-extra": "^11.1.1",
+ "js-yaml": "^4.1.0",
+ "lodash": "^4.17.21",
+ "schema-dts": "^1.1.2",
+ "tslib": "^2.6.0",
+ "utility-types": "^3.10.0",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/plugin-content-docs/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
- "requires": {
- "browserify-aes": "^1.0.4",
- "browserify-des": "^1.0.0",
- "evp_bytestokey": "^1.0.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "browserify-des": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
- "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "node_modules/@docusaurus/plugin-content-pages": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.2.tgz",
+ "integrity": "sha512-h5R12sZ/vV9EPiVjvIl9YFCOwkpwXes7dQMYt3EvP6Pphu4amHxxTqWxf08Fl5DR8h+oZMbWpFTNw5vKEYfvzQ==",
"dev": true,
- "requires": {
- "cipher-base": "^1.0.1",
- "des.js": "^1.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
- },
+ "license": "MIT",
"dependencies": {
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
- "dev": true
- }
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "fs-extra": "^11.1.1",
+ "tslib": "^2.6.0",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
- "browserify-rsa": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
- "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "node_modules/@docusaurus/plugin-css-cascade-layers": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.2.tgz",
+ "integrity": "sha512-UkdvQby5OQUKWrw3lLnSTJXQ6VETaUVTuPQX9AABtmFm5h+ifEBx1OQ+LN726Q4byuwBf2ElHkf4qU4hTxdvRg==",
"dev": true,
- "requires": {
- "bn.js": "^4.1.0",
- "randombytes": "^2.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
}
},
- "browserify-sign": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
- "dev": true,
- "requires": {
- "bn.js": "^4.1.1",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.2",
- "elliptic": "^6.0.0",
- "inherits": "^2.0.1",
- "parse-asn1": "^5.0.0"
+ "node_modules/@docusaurus/plugin-debug": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.10.2.tgz",
+ "integrity": "sha512-8vbZNOSCpnsT57EY6CgN7sgRVmx3KTYwO8Uvo2pbxOyb8tbqAwtT9SslqaQ41HbA1v1hpn5RP7u5s2KvRwAFpQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "fs-extra": "^11.1.1",
+ "react-json-view-lite": "^2.3.0",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
- "browserify-zlib": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
- "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "node_modules/@docusaurus/plugin-google-analytics": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.2.tgz",
+ "integrity": "sha512-kMHMBK9j4VAtgd5owwrRLRIi0EjkrpXlX7ePj1+y68XfVZV9I1T4S+koPDm+Hfw2TtnyHvh0uNrDvjz+DjQGVA==",
"dev": true,
- "requires": {
- "pako": "~1.0.5"
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
- "browserslist": {
- "version": "4.9.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz",
- "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001030",
- "electron-to-chromium": "^1.3.363",
- "node-releases": "^1.1.50"
- },
- "dependencies": {
- "node-releases": {
- "version": "1.1.52",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.52.tgz",
- "integrity": "sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==",
- "dev": true,
- "requires": {
- "semver": "^6.3.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "node_modules/@docusaurus/plugin-google-gtag": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.2.tgz",
+ "integrity": "sha512-Vt90nNFhtAChRe9+it1hcHFgFvETdSnOkL5Bma+p6E/yU2tAYrvvyk+gv+LJGM2ZUkyKuKXLRsZ2Lb0bO7+Vog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
- "btoa-lite": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
- "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=",
- "dev": true
- },
- "buffer": {
- "version": "4.9.2",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
- "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "node_modules/@docusaurus/plugin-google-tag-manager": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.2.tgz",
+ "integrity": "sha512-MLCffCldysi/R0nzJQP7ZWd0xAoGNnSTiVOo6TTR6mKVGFhE+/XArGe67ZcaZv1uytgQXoXs92VJrgVDrz80rQ==",
"dev": true,
- "requires": {
- "base64-js": "^1.0.2",
- "ieee754": "^1.1.4",
- "isarray": "^1.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- }
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
- "buffer-alloc": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
- "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "node_modules/@docusaurus/plugin-sitemap": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.2.tgz",
+ "integrity": "sha512-PODkwg5XetLML3hU/3xpCKJUZ9cqExLaBnD/Fzzwj2VHogLeqnDisLIujae87zuze7T4mCm2A6KEqZkyiz07EQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "fs-extra": "^11.1.1",
+ "sitemap": "^7.1.1",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/plugin-svgr": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.2.tgz",
+ "integrity": "sha512-JgfT3jWM0TJ8Uw0cEcqxHpybngQY1vlBYpuuNO+gEh5iPh5Ar+vxq/u9CFrYsWeXy48BN7Db76Pzp2edNXUQ8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "@svgr/core": "8.1.0",
+ "@svgr/webpack": "^8.1.0",
+ "tslib": "^2.6.0",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/preset-classic": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.10.2.tgz",
+ "integrity": "sha512-a4B3VczmDl99zK0EufDQYomdJ186WDingjmDXxhN2PNPS9Ty/Y2M5CLFX1KQMRKqRTLiRDKfutzG5IY1FC/ceg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/plugin-content-blog": "3.10.2",
+ "@docusaurus/plugin-content-docs": "3.10.2",
+ "@docusaurus/plugin-content-pages": "3.10.2",
+ "@docusaurus/plugin-css-cascade-layers": "3.10.2",
+ "@docusaurus/plugin-debug": "3.10.2",
+ "@docusaurus/plugin-google-analytics": "3.10.2",
+ "@docusaurus/plugin-google-gtag": "3.10.2",
+ "@docusaurus/plugin-google-tag-manager": "3.10.2",
+ "@docusaurus/plugin-sitemap": "3.10.2",
+ "@docusaurus/plugin-svgr": "3.10.2",
+ "@docusaurus/theme-classic": "3.10.2",
+ "@docusaurus/theme-common": "3.10.2",
+ "@docusaurus/theme-search-algolia": "3.10.2",
+ "@docusaurus/types": "3.10.2"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/theme-classic": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.10.2.tgz",
+ "integrity": "sha512-JqTSLQmqmA9uKWZsD5iwBGJ4JyKB4/yTw6PsSXVPRJG/6GAm/u+add9Iip+hvwP12/AnPNztrdxsI14NJW4KeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/module-type-aliases": "3.10.2",
+ "@docusaurus/plugin-content-blog": "3.10.2",
+ "@docusaurus/plugin-content-docs": "3.10.2",
+ "@docusaurus/plugin-content-pages": "3.10.2",
+ "@docusaurus/theme-common": "3.10.2",
+ "@docusaurus/theme-translations": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "@mdx-js/react": "^3.0.0",
+ "clsx": "^2.0.0",
+ "copy-text-to-clipboard": "^3.2.0",
+ "infima": "0.2.0-alpha.45",
+ "lodash": "^4.17.21",
+ "nprogress": "^0.2.0",
+ "postcss": "^8.5.4",
+ "prism-react-renderer": "^2.3.0",
+ "prismjs": "^1.29.0",
+ "react-router-dom": "^5.3.4",
+ "rtlcss": "^4.1.0",
+ "tslib": "^2.6.0",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/theme-common": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.10.2.tgz",
+ "integrity": "sha512-R9b/vMpK1yye6hNZTA6x/ivRv+at6GhxnXcxkpzCGzO1R1RwiquqiFg2wMFh6aqlJTpWRFKpFD2TzCDQcyOU0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/mdx-loader": "3.10.2",
+ "@docusaurus/module-type-aliases": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router-config": "*",
+ "clsx": "^2.0.0",
+ "parse-numeric-range": "^1.3.0",
+ "prism-react-renderer": "^2.3.0",
+ "tslib": "^2.6.0",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "@docusaurus/plugin-content-docs": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/theme-search-algolia": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.2.tgz",
+ "integrity": "sha512-1msxllyhi/5m77JukXtp5UFnUAriwZIC1oJ7MTnpQpCwLTbclJi5BK5n28CTZuSXpQN2ewbbnqRgAhMM6c6ihg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-core": "^1.19.2",
+ "@docsearch/react": "^3.9.0 || ^4.3.2",
+ "@docusaurus/core": "3.10.2",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/plugin-content-docs": "3.10.2",
+ "@docusaurus/theme-common": "3.10.2",
+ "@docusaurus/theme-translations": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-validation": "3.10.2",
+ "algoliasearch": "^5.37.0",
+ "algoliasearch-helper": "^3.26.0",
+ "clsx": "^2.0.0",
+ "eta": "^2.2.0",
+ "fs-extra": "^11.1.1",
+ "lodash": "^4.17.21",
+ "tslib": "^2.6.0",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/theme-translations": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.10.2.tgz",
+ "integrity": "sha512-iv20wrxnyXkY89LM3TzRlzGlt5fIGO5UnaR6UL1ZVfB9RRFjxQFQ6awDrwAc6Km8Y5gD8pInuwYPF+6/TiCxXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fs-extra": "^11.1.1",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ }
+ },
+ "node_modules/@docusaurus/types": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.10.2.tgz",
+ "integrity": "sha512-B6rvfwIFSapUqUJjMriZswX13K8l5Z7AcmVE6uTEJpYddQieSTR12DsGaFtcZAIDsQd4p+0WTl0Vc6jmZK0Trw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@mdx-js/mdx": "^3.0.0",
+ "@types/history": "^4.7.11",
+ "@types/mdast": "^4.0.2",
+ "@types/react": "*",
+ "commander": "^5.1.0",
+ "joi": "^17.9.2",
+ "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0",
+ "utility-types": "^3.10.0",
+ "webpack": "^5.95.0",
+ "webpack-merge": "^5.9.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@docusaurus/types/node_modules/joi": {
+ "version": "17.13.4",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.4.tgz",
+ "integrity": "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==",
"dev": true,
- "requires": {
- "buffer-alloc-unsafe": "^1.1.0",
- "buffer-fill": "^1.0.0"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.3.0",
+ "@hapi/topo": "^5.1.0",
+ "@sideway/address": "^4.1.5",
+ "@sideway/formula": "^3.0.1",
+ "@sideway/pinpoint": "^2.0.0"
}
},
- "buffer-alloc-unsafe": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
- "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
- "dev": true
- },
- "buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
- "dev": true
- },
- "buffer-equal-constant-time": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
- "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=",
- "dev": true
- },
- "buffer-fill": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
- "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
- "dev": true
- },
- "buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
- "dev": true
+ "node_modules/@docusaurus/types/node_modules/webpack-merge": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",
+ "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "flat": "^5.0.2",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
},
- "buffer-indexof": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
- "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
- "dev": true
+ "node_modules/@docusaurus/utils": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.10.2.tgz",
+ "integrity": "sha512-xx0W3eav2uW1NRIpuHJWNwLTC15xPNjU4Uxi9NSnd3swYC96BE3vFiT93SD8s24kmAAWNwgZwfZ2fghGZ01Lcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@11ty/gray-matter": "^1.0.0",
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/types": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "escape-string-regexp": "^4.0.0",
+ "execa": "^5.1.1",
+ "file-loader": "^6.2.0",
+ "fs-extra": "^11.1.1",
+ "github-slugger": "^1.5.0",
+ "globby": "^11.1.0",
+ "jiti": "^1.20.0",
+ "js-yaml": "^4.1.0",
+ "lodash": "^4.17.21",
+ "micromatch": "^4.0.5",
+ "p-queue": "^6.6.2",
+ "prompts": "^2.4.2",
+ "resolve-pathname": "^3.0.0",
+ "tslib": "^2.6.0",
+ "url-loader": "^4.1.1",
+ "utility-types": "^3.10.0",
+ "webpack": "^5.88.1"
+ },
+ "engines": {
+ "node": ">=20.0"
+ }
},
- "buffer-xor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
- "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
- "dev": true
+ "node_modules/@docusaurus/utils-common": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.10.2.tgz",
+ "integrity": "sha512-x3Dz6jv6iQKBNjBmVTu8p57abMp/VNTUgKBMgRVXJc5444orBTsArv0+cdfrXTiz/VMmHfDRVkPbL7GH2B7T7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/types": "3.10.2",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ }
},
- "builtin-modules": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
- "dev": true
+ "node_modules/@docusaurus/utils-validation": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.10.2.tgz",
+ "integrity": "sha512-sn8unbDfUL585NtR3cwHefPicOyaHvPaX7VD0aOg/siIxUBoKyKKaGEqzJZDS64mM43TnxurkYDtmB1wsJlZsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/logger": "3.10.2",
+ "@docusaurus/utils": "3.10.2",
+ "@docusaurus/utils-common": "3.10.2",
+ "fs-extra": "^11.2.0",
+ "joi": "^17.9.2",
+ "js-yaml": "^4.1.0",
+ "lodash": "^4.17.21",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0"
+ }
},
- "builtin-status-codes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
- "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
- "dev": true
+ "node_modules/@docusaurus/utils-validation/node_modules/joi": {
+ "version": "17.13.3",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
+ "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.3.0",
+ "@hapi/topo": "^5.1.0",
+ "@sideway/address": "^4.1.5",
+ "@sideway/formula": "^3.0.1",
+ "@sideway/pinpoint": "^2.0.0"
+ }
},
- "bytes": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
- "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
- },
- "cacache": {
- "version": "12.0.3",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
- "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
- "dev": true,
- "requires": {
- "bluebird": "^3.5.5",
- "chownr": "^1.1.1",
- "figgy-pudding": "^3.5.1",
- "glob": "^7.1.4",
- "graceful-fs": "^4.1.15",
- "infer-owner": "^1.0.3",
- "lru-cache": "^5.1.1",
- "mississippi": "^3.0.0",
- "mkdirp": "^0.5.1",
- "move-concurrently": "^1.0.1",
- "promise-inflight": "^1.0.1",
- "rimraf": "^2.6.3",
- "ssri": "^6.0.1",
- "unique-filename": "^1.1.1",
- "y18n": "^4.0.0"
- },
- "dependencies": {
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
- "dev": true
- },
- "lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "requires": {
- "yallist": "^3.0.2"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
+ "node_modules/@docusaurus/utils-validation/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
},
- "yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
}
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "node_modules/@docusaurus/utils/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
- "requires": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
},
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@docusaurus/utils/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "component-emitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
- "dev": true
- }
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "cache-manager": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-2.11.1.tgz",
- "integrity": "sha512-XhUuc9eYwkzpK89iNewFwtvcDYMUsvtwzHeyEOPJna/WsVsXcrzsA1ft2M0QqPNunEzLhNCYPo05tEfG+YuNow==",
+ "node_modules/@docusaurus/utils/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true,
- "requires": {
- "async": "1.5.2",
- "lodash.clonedeep": "4.5.0",
- "lru-cache": "4.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
},
- "dependencies": {
- "lru-cache": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.0.tgz",
- "integrity": "sha1-tcvwFVbBaWb+vlTO7A+03JDfbCg=",
- "dev": true,
- "requires": {
- "pseudomap": "^1.0.1",
- "yallist": "^2.0.0"
- }
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "cache-manager-fs-hash": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/cache-manager-fs-hash/-/cache-manager-fs-hash-0.0.7.tgz",
- "integrity": "sha512-7X+FPItAJf1tKKqJx6ljDJQc0fgSR5B+KPxFQLj+vYSL4q9XdrCbZldgsNb6wueRuIooj01wt0FubB08zaefRg==",
+ "node_modules/@docusaurus/utils/node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/@docusaurus/utils/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
- "requires": {
- "es6-promisify": "^6.0.0",
- "lockfile": "^1.0.4"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
},
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@easyops-cn/autocomplete.js": {
+ "version": "0.38.1",
+ "resolved": "https://registry.npmjs.org/@easyops-cn/autocomplete.js/-/autocomplete.js-0.38.1.tgz",
+ "integrity": "sha512-drg76jS6syilOUmVNkyo1c7ZEBPcPuK+aJA7AksM5ZIIbV57DMHCywiCr+uHyv8BE5jUTU98j/H7gVrkHrWW3Q==",
+ "dev": true,
"dependencies": {
- "es6-promisify": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz",
- "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==",
- "dev": true
+ "cssesc": "^3.0.0",
+ "immediate": "^3.2.3"
+ }
+ },
+ "node_modules/@easyops-cn/docusaurus-search-local": {
+ "version": "0.55.2",
+ "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.55.2.tgz",
+ "integrity": "sha512-dI/riu+MbDxkAjAHAdc0uahjXRaWKvbIPe9IAmA6AGcUfnVb9xd8s2I/6wEPTOXsAd6eFqn4Yis3WBWh3KUd3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/plugin-content-docs": "^2 || ^3",
+ "@docusaurus/theme-translations": "^2 || ^3",
+ "@docusaurus/utils": "^2 || ^3",
+ "@docusaurus/utils-common": "^2 || ^3",
+ "@docusaurus/utils-validation": "^2 || ^3",
+ "@easyops-cn/autocomplete.js": "^0.38.1",
+ "@node-rs/jieba": "^1.6.0",
+ "cheerio": "^1.0.0",
+ "clsx": "^2.1.1",
+ "comlink": "^4.4.2",
+ "debug": "^4.2.0",
+ "fs-extra": "^10.0.0",
+ "klaw-sync": "^6.0.0",
+ "lunr": "^2.3.9",
+ "lunr-languages": "^1.4.0",
+ "mark.js": "^8.11.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "@docusaurus/theme-common": "^2 || ^3",
+ "open-ask-ai": "^0.7.3",
+ "react": "^16.14.0 || ^17 || ^18 || ^19",
+ "react-dom": "^16.14.0 || 17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "open-ask-ai": {
+ "optional": true
}
}
},
- "cacheable-request": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.0.0.tgz",
- "integrity": "sha512-2N7AmszH/WPPpl5Z3XMw1HAP+8d+xugnKQAeKvxFZ/04dbT/CAznqwbl+7eSr3HkwdepNwtb2yx3CAMQWvG01Q==",
+ "node_modules/@easyops-cn/docusaurus-search-local/node_modules/cheerio": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
+ "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
"dev": true,
- "requires": {
- "clone-response": "^1.0.2",
- "get-stream": "^4.0.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^3.0.0",
- "lowercase-keys": "^1.0.1",
- "normalize-url": "^3.1.0",
- "responselike": "^1.0.2"
- },
"dependencies": {
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- }
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.1.0",
+ "encoding-sniffer": "^0.2.0",
+ "htmlparser2": "^9.1.0",
+ "parse5": "^7.1.2",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0",
+ "parse5-parser-stream": "^7.1.2",
+ "undici": "^6.19.5",
+ "whatwg-mimetype": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.17"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
- "cachedir": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz",
- "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==",
- "dev": true
+ "node_modules/@easyops-cn/docusaurus-search-local/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
- "caching-transform": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
- "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
- "dev": true,
- "requires": {
- "hasha": "^5.0.0",
- "make-dir": "^3.0.0",
- "package-hash": "^4.0.0",
- "write-file-atomic": "^3.0.0"
- },
- "dependencies": {
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
+ "node_modules/@easyops-cn/docusaurus-search-local/node_modules/htmlparser2": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
+ "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
}
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.1.0",
+ "entities": "^4.5.0"
}
},
- "call-me-maybe": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
- "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
- "dev": true
- },
- "caller": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/caller/-/caller-1.0.1.tgz",
- "integrity": "sha1-uFGGD3Dhlds9J3OVqhp+I+ow7PU=",
- "dev": true
- },
- "caller-callsite": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
- "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "node_modules/@emnapi/core": {
+ "version": "0.45.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz",
+ "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==",
"dev": true,
- "requires": {
- "callsites": "^2.0.0"
- },
+ "optional": true,
"dependencies": {
- "callsites": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
- "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
- "dev": true
- }
+ "tslib": "^2.4.0"
}
},
- "caller-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
- "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "node_modules/@emnapi/runtime": {
+ "version": "0.45.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz",
+ "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==",
"dev": true,
- "requires": {
- "caller-callsite": "^2.0.0"
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
- "callsite": {
+ "node_modules/@epic-web/invariant": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
- "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
- },
- "callsites": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
- "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
+ "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
+ "license": "MIT"
},
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ "node_modules/@es-joy/jsdoccomment": {
+ "version": "0.88.0",
+ "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.88.0.tgz",
+ "integrity": "sha512-GK/HL/claLLNo5KG705auIlZMwEtmn88ofSGuLsmVZwKBqMPJhW9DiznYNq07QEqz9BPtA3LBfYImtZmhVvRAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.9",
+ "@typescript-eslint/types": "^8.59.4",
+ "comment-parser": "1.4.7",
+ "esquery": "^1.7.0",
+ "jsdoc-type-pratt-parser": "~7.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
},
- "camelcase-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz",
- "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=",
+ "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
"dev": true,
- "requires": {
- "camelcase": "^4.1.0",
- "map-obj": "^2.0.0",
- "quick-lru": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
- "dependencies": {
- "camelcase": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
- "dev": true
- }
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "camelize": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
- "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=",
- "dev": true
+ "node_modules/@es-joy/resolve.exports": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz",
+ "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
},
- "camelo": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/camelo/-/camelo-1.1.12.tgz",
- "integrity": "sha512-qyY6IgFJUglfYeyL5drpjFMbaRjFRTTFeYRmoSkOCf+lazypMz3IcZk4HaE2NZra/b9XdSQi2W01HmaZymNoxA==",
- "requires": {
- "regex-escape": "^3.3.0",
- "uc-first-array": "^1.0.0"
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
- "caniuse-api": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "caniuse-lite": "^1.0.0",
- "lodash.memoize": "^4.1.2",
- "lodash.uniq": "^4.5.0"
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "caniuse-lite": {
- "version": "1.0.30001035",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz",
- "integrity": "sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==",
- "dev": true
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
},
- "capture-stack-trace": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
- "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
},
- "caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
},
- "catharsis": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz",
- "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==",
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "requires": {
- "lodash": "^4.17.14"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "chai": {
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
- "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
- "requires": {
- "assertion-error": "^1.1.0",
- "check-error": "^1.0.2",
- "deep-eql": "^3.0.1",
- "get-func-name": "^2.0.0",
- "pathval": "^1.1.0",
- "type-detect": "^4.0.5"
- },
- "dependencies": {
- "assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
},
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
}
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "chai-as-promised": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
- "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
- "requires": {
- "check-error": "^1.0.2"
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
}
},
- "chai-datetime": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/chai-datetime/-/chai-datetime-1.5.0.tgz",
- "integrity": "sha1-N0LxiwJMdbdqK37uKRZiMkRnWWw=",
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
- "requires": {
- "chai": ">1.9.0"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "chai-enzyme": {
- "version": "1.0.0-beta.1",
- "resolved": "https://registry.npmjs.org/chai-enzyme/-/chai-enzyme-1.0.0-beta.1.tgz",
- "integrity": "sha512-vWT101M7qjq6kM/29G4vHrgLM4Mj1gCnKuvOSF03s8pFVsqol4B6USoGM/aYRKqaaIHs8/AxmHjWGFplQWhIQw==",
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
- "requires": {
- "html": "^1.0.0"
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "chai-string": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz",
- "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==",
- "dev": true
+ "node_modules/@exodus/schemasafe": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz",
+ "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==",
+ "dev": true,
+ "license": "MIT"
},
- "chai-subset": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz",
- "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=",
- "dev": true
+ "node_modules/@faker-js/faker": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz",
+ "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==",
+ "deprecated": "Please update to a newer version.",
+ "dev": true,
+ "license": "MIT"
},
- "chai-things": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/chai-things/-/chai-things-0.2.0.tgz",
- "integrity": "sha1-xVEoN4+bs5nplPAAUhUZhO1uvnA=",
- "dev": true
+ "node_modules/@file-type/xml": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@file-type/xml/-/xml-0.4.3.tgz",
+ "integrity": "sha512-pGRmkHf+NofNy/52r06HOTsEwdNnBsFEhN6U95s33P+ezuoxZEyBTV9lOB1/Zr0So6/9vDVfWZXLpgd0fy8cOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sax": "^1.4.1",
+ "strtok3": "^10.2.2"
+ }
},
- "chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
+ "node_modules/@gitbeaker/core": {
+ "version": "38.12.1",
+ "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-38.12.1.tgz",
+ "integrity": "sha512-8XMVcBIdVAAoxn7JtqmZ2Ee8f+AZLcCPmqEmPFOXY2jPS84y/DERISg/+sbhhb18iRy+ZsZhpWgQ/r3CkYNJOQ==",
+ "dev": true,
+ "dependencies": {
+ "@gitbeaker/requester-utils": "^38.12.1",
+ "qs": "^6.11.1",
+ "xcase": "^2.0.1"
},
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@gitbeaker/requester-utils": {
+ "version": "38.12.1",
+ "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-38.12.1.tgz",
+ "integrity": "sha512-Rc/DgngS0YPN+AY1s9UnexKSy4Lh0bkQVAq9p7PRbRpXb33SlTeCg8eg/8+A/mrMcHgYmP0XhH8lkizyA5tBUQ==",
+ "dev": true,
"dependencies": {
- "ansi-styles": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz",
- "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==",
- "requires": {
- "@types/color-name": "^1.1.1",
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
+ "qs": "^6.11.1",
+ "xcase": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
- "char-width-table-consumer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
- "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
- "requires": {
- "binary-search": "^1.3.5"
+ "node_modules/@gitbeaker/rest": {
+ "version": "38.12.1",
+ "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-38.12.1.tgz",
+ "integrity": "sha512-9KMSDtJ/sIov+5pcH+CAfiJXSiuYgN0KLKQFg0HHWR2DwcjGYkcbmhoZcWsaOWOqq4kihN1l7wX91UoRxxKKTQ==",
+ "dev": true,
+ "dependencies": {
+ "@gitbeaker/core": "^38.12.1",
+ "@gitbeaker/requester-utils": "^38.12.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
- "chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
+ "node_modules/@hapi/address": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.1.0.tgz",
+ "integrity": "sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ==",
+ "deprecated": "Moved to 'npm install @sideway/address'",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0"
+ }
},
- "charenc": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
- "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=",
- "dev": true
+ "node_modules/@hapi/formula": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz",
+ "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==",
+ "deprecated": "Moved to 'npm install @sideway/formula'",
+ "dev": true,
+ "optional": true
},
- "check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
+ "node_modules/@hapi/hoek": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
+ "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
+ "dev": true
},
- "check-more-types": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
- "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=",
- "dev": true
- },
- "check-node-version": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/check-node-version/-/check-node-version-4.0.3.tgz",
- "integrity": "sha512-kbnOaF7SE1uasWx3hqxzsU2yz7I+rkxSMuTW0eKoF39eUtSR/a0F4Sm35LwJNx4itOIARf9eLhWHOOh7rlZ5/g==",
- "requires": {
- "chalk": "^3.0.0",
- "map-values": "^1.0.1",
- "minimist": "^1.2.0",
- "object-filter": "^1.0.2",
- "run-parallel": "^1.1.4",
- "semver": "^6.3.0"
- },
+ "node_modules/@hapi/joi": {
+ "version": "17.1.1",
+ "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz",
+ "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==",
+ "deprecated": "Switch to 'npm install joi'",
+ "dev": true,
+ "optional": true,
"dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
- }
+ "@hapi/address": "^4.0.1",
+ "@hapi/formula": "^2.0.0",
+ "@hapi/hoek": "^9.0.0",
+ "@hapi/pinpoint": "^2.0.0",
+ "@hapi/topo": "^5.0.0"
}
},
- "check-types": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/check-types/-/check-types-10.0.0.tgz",
- "integrity": "sha512-FeKh/xUITytmx5fC709lzTnUJPDjeOrVZJ1cvkLwtxXJp8g0z7FY5heK5w1Iy8cYV5n563wCcnnxMjkAc4JzFA==",
- "dev": true
+ "node_modules/@hapi/pinpoint": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
+ "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q=="
},
- "cheerio": {
- "version": "1.0.0-rc.3",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
- "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
- "dev": true,
- "requires": {
- "css-select": "~1.2.0",
- "dom-serializer": "~0.1.1",
- "entities": "~1.1.1",
- "htmlparser2": "^3.9.1",
- "lodash": "^4.15.0",
- "parse5": "^3.0.1"
- },
- "dependencies": {
- "dom-serializer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
- "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
- "dev": true,
- "requires": {
- "domelementtype": "^1.3.0",
- "entities": "^1.1.1"
- }
- },
- "htmlparser2": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
- "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
- "dev": true,
- "requires": {
- "domelementtype": "^1.3.1",
- "domhandler": "^2.3.0",
- "domutils": "^1.5.1",
- "entities": "^1.1.1",
- "inherits": "^2.0.1",
- "readable-stream": "^3.1.1"
- },
- "dependencies": {
- "domelementtype": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
- "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
- "dev": true
- }
- }
- },
- "readable-stream": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
- "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
- "dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
- "dev": true
- },
- "string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.2.0"
- }
- }
+ "node_modules/@hapi/tlds": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.2.tgz",
+ "integrity": "sha512-1jkwm1WY9VIb6WhdANRmWDkXQUcIRpxqZpSdS+HD9vhoVL3zwoFvP8doQNEgT6k3VST0Ewu50wZnXIceRYp5tw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=14.0.0"
}
},
- "child-process-promise": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz",
- "integrity": "sha1-RzChHvYQ+tRQuPIjx50x172tgHQ=",
+ "node_modules/@hapi/topo": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
+ "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
"dev": true,
- "requires": {
- "cross-spawn": "^4.0.2",
- "node-version": "^1.0.0",
- "promise-polyfill": "^6.0.1"
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0"
}
},
- "chokidar": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
- "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"dev": true,
- "requires": {
- "anymatch": "~3.1.1",
- "braces": "~3.0.2",
- "fsevents": "~2.1.1",
- "glob-parent": "~5.1.0",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.2.0"
- },
- "dependencies": {
- "anymatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
- "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "binary-extensions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
- "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
- "dev": true
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "fsevents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
- "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
- "dev": true,
- "optional": true
- },
- "glob-parent": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
- "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "readdirp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
- "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
- "dev": true,
- "requires": {
- "picomatch": "^2.0.4"
- }
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- }
+ "engines": {
+ "node": ">=18.18.0"
}
},
- "chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "dev": true
- },
- "chrome-trace-event": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
- "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
"dev": true,
- "requires": {
- "tslib": "^1.9.0"
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
}
},
- "chrome-web-store-item-property": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/chrome-web-store-item-property/-/chrome-web-store-item-property-1.2.0.tgz",
- "integrity": "sha512-I3OUL6dHOfYUAC1MgoR4xh07O2B0OUf8CWCZYL2QiItCy2Z0zUFyHWir8d14OLUoJw/cMl1GNb5ITPJCCa6wJw==",
- "requires": {
- "array-includes": "^3.0.3",
- "create-error-class": "^3.0.0",
- "es6-promise": "^4.2.8",
- "htmlparser2": "^4.0.0",
- "is-ok": "^1.0.1",
- "node-status-codes": "^3.0.0",
- "object-assign": "^4.1.1",
- "request": "^2.88.0"
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
},
- "dependencies": {
- "dom-serializer": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
- "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
- "requires": {
- "domelementtype": "^2.0.1",
- "entities": "^2.0.0"
- }
- },
- "domelementtype": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
- "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
- },
- "domhandler": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz",
- "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==",
- "requires": {
- "domelementtype": "^2.0.1"
- }
- },
- "domutils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz",
- "integrity": "sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==",
- "requires": {
- "dom-serializer": "^0.2.1",
- "domelementtype": "^2.0.1",
- "domhandler": "^3.0.0"
- }
- },
- "entities": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
- "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
- },
- "htmlparser2": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.0.0.tgz",
- "integrity": "sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ==",
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^3.0.0",
- "domutils": "^2.0.0",
- "entities": "^2.0.0"
- }
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "cipher-base": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
- "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "node_modules/@humanwhocodes/gitignore-to-minimatch": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
+ "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "clap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
- "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
- "dev": true,
- "requires": {
- "chalk": "^1.1.3"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- }
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
- "dev": true,
- "requires": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "dev": true
- }
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "dev": true
- },
- "cli-boxes": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz",
- "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==",
- "dev": true
- },
- "cli-cursor": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
- "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
- "requires": {
- "restore-cursor": "^2.0.0"
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "cli-spinners": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz",
- "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==",
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
- "optional": true
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
},
- "cli-table3": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
- "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
- "requires": {
- "colors": "^1.1.2",
- "object-assign": "^4.1.0",
- "string-width": "^2.1.1"
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "cli-truncate": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
- "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
- "dev": true,
- "requires": {
- "slice-ansi": "0.0.4",
- "string-width": "^1.0.1"
- },
- "dependencies": {
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "slice-ansi": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
- "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
- "dev": true
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- }
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
- "cli-width": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
- "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
- "dev": true
- },
- "clipboard-copy": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.1.0.tgz",
- "integrity": "sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA==",
- "dev": true
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
},
- "clipboardy": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.2.0.tgz",
- "integrity": "sha512-9ry9nC3VFULNmoEIqvuRwCIQ9M7wjnm4O+yvk7xkmhR+7FAUWaeX751oeYJbORg0h0zmqW1EVDoZK8f7yapwbg==",
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
"dev": true,
- "requires": {
- "arch": "^2.1.1",
- "execa": "^1.0.0",
- "is-wsl": "^2.1.1"
- },
"dependencies": {
- "is-wsl": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
- "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==",
- "dev": true
- }
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "cliui": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
- "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
"dev": true,
- "requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0",
- "wrap-ansi": "^2.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- },
- "wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
- "dev": true,
- "requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- }
- }
- }
- }
- },
- "clone-response": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
- "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
- "mimic-response": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "cluster-key-slot": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
- "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
+ "node_modules/@jest/types/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
},
- "coa": {
+ "node_modules/@jest/types/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz",
- "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
- "requires": {
- "q": "^1.1.2"
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
- "dev": true
+ "node_modules/@jest/types/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
},
- "collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "node_modules/@jest/types/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "requires": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "color": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
- "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
- "requires": {
- "color-convert": "^1.9.1",
- "color-string": "^1.5.2"
- },
+ "license": "MIT",
"dependencies": {
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- }
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
}
},
- "color-convert": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
- "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"dev": true,
- "requires": {
- "color-name": "^1.1.1"
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
- },
- "color-string": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
- "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
"dev": true,
- "requires": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
}
},
- "colors": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
- "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
- "dev": true
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
},
- "combined-stream": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
- "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
- "requires": {
- "delayed-stream": "~1.0.0"
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "command-exists": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz",
- "integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==",
- "dev": true
- },
- "commander": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
- "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
- "dev": true
- },
- "comment-parser": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.2.tgz",
- "integrity": "sha512-4Rjb1FnxtOcv9qsfuaNuVsmmVn4ooVoBHzYfyKteiXwIU84PClyGA5jASoFMwPV93+FPh9spwueXauxFJZkGAg==",
- "dev": true
+ "node_modules/@jsdoc/salty": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
+ "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">=v12.0.0"
+ }
},
- "common-tags": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
- "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
- "dev": true
+ "node_modules/@jsep-plugin/assignment": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz",
+ "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==",
+ "engines": {
+ "node": ">= 10.16.0"
+ },
+ "peerDependencies": {
+ "jsep": "^0.4.0||^1.0.0"
+ }
},
- "commondir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
- "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
- "dev": true
+ "node_modules/@jsep-plugin/regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz",
+ "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==",
+ "engines": {
+ "node": ">= 10.16.0"
+ },
+ "peerDependencies": {
+ "jsep": "^0.4.0||^1.0.0"
+ }
},
- "component-bind": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
- "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+ "node_modules/@jsonjoy.com/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "component-emitter": {
+ "node_modules/@jsonjoy.com/buffers": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
- },
- "component-inherit": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
- "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz",
+ "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "node_modules/@jsonjoy.com/codegen": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz",
+ "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==",
"dev": true,
- "requires": {
- "mime-db": ">= 1.43.0 < 2"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
},
- "dependencies": {
- "mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
- "dev": true
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "compression": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
- "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "node_modules/@jsonjoy.com/fs-core": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.56.2.tgz",
+ "integrity": "sha512-5s3t0Lj/gDgPhhXEdSe9yNDB07iMrpIXN9OV9FTiwlLKP3EBFhsbOhhMMVoWuSJkPxaaiOFUpZcyZcKi7mOmUQ==",
"dev": true,
- "requires": {
- "accepts": "~1.3.5",
- "bytes": "3.0.0",
- "compressible": "~2.0.16",
- "debug": "2.6.9",
- "on-headers": "~1.0.2",
- "safe-buffer": "5.1.2",
- "vary": "~1.1.2"
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/fs-node-builtins": "4.56.2",
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "thingies": "^2.5.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
},
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/@jsonjoy.com/fs-fsa": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.56.2.tgz",
+ "integrity": "sha512-2lN4rdhcjFBf2Oji0rHR1aS+fW+GA0l9o9gXCMWFoC+YXqRO4N4xkSeJwm6a10SMuqlhoseCWRWlhaDYiNiI2A==",
+ "dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
- "dev": true
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- }
+ "@jsonjoy.com/fs-core": "4.56.2",
+ "@jsonjoy.com/fs-node-builtins": "4.56.2",
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "thingies": "^2.5.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ "node_modules/@jsonjoy.com/fs-node": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.56.2.tgz",
+ "integrity": "sha512-Ws4cwm9UQY0noP/Ee2KpPf2zJJukJywjTIl3lBTH/AdH7r5n5CyGPLgySxpAa7/isV0WD02bYV+XKhslF/Dtbg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/fs-core": "4.56.2",
+ "@jsonjoy.com/fs-node-builtins": "4.56.2",
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "@jsonjoy.com/fs-print": "4.56.2",
+ "glob-to-regex.js": "^1.0.0",
+ "thingies": "^2.5.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "concat-stream": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
- "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+ "node_modules/@jsonjoy.com/fs-node-builtins": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.56.2.tgz",
+ "integrity": "sha512-TB8rFES/4lygIudoTHSGp2fjHe7R229VRQ4IQCMds6uTKhBKuDLZAqOUBiS3hosfxTVrB/JpDrr46MvCSjPzog==",
"dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
},
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
- "readable-stream": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
- "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
- "dev": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~1.0.6",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.0.3",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "concurrently": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.1.0.tgz",
- "integrity": "sha512-9ViZMu3OOCID3rBgU31mjBftro2chOop0G2u1olq1OuwRBVRw/GxHTg80TVJBUTJfoswMmEUeuOg1g1yu1X2dA==",
+ "node_modules/@jsonjoy.com/fs-node-to-fsa": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.56.2.tgz",
+ "integrity": "sha512-Es62G93ychdl0VhQKVTIPq31QWabXveTEVJfi3gC/AIiehnXV3AMl38TWXLCS4fomBz5EaLqNhMkV7u/oW1p6g==",
"dev": true,
- "requires": {
- "chalk": "^2.4.2",
- "date-fns": "^2.0.1",
- "lodash": "^4.17.15",
- "read-pkg": "^4.0.1",
- "rxjs": "^6.5.2",
- "spawn-command": "^0.0.2-1",
- "supports-color": "^6.1.0",
- "tree-kill": "^1.2.2",
- "yargs": "^13.3.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "date-fns": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.9.0.tgz",
- "integrity": "sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
- "dev": true,
- "requires": {
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1"
- }
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "read-pkg": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
- "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
- "dev": true,
- "requires": {
- "normalize-package-data": "^2.3.2",
- "parse-json": "^4.0.0",
- "pify": "^3.0.0"
- }
- },
- "supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/fs-fsa": "4.56.2",
+ "@jsonjoy.com/fs-node-builtins": "4.56.2",
+ "@jsonjoy.com/fs-node-utils": "4.56.2"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "config": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz",
- "integrity": "sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==",
- "requires": {
- "json5": "^2.1.1"
- },
+ "node_modules/@jsonjoy.com/fs-node-utils": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.56.2.tgz",
+ "integrity": "sha512-CIUSlhbnws7b9f3Z2r963/lSA+VLPJlJcy8fqjQ9lk1Z1y6Ca9qj2CWXlABkvDZE7sDX+6PEdEU1PsXlfkZVbg==",
+ "dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "json5": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
- "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
- "requires": {
- "minimist": "^1.2.5"
- }
- }
+ "@jsonjoy.com/fs-node-builtins": "4.56.2"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "configstore": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
- "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
+ "node_modules/@jsonjoy.com/fs-print": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.56.2.tgz",
+ "integrity": "sha512-7e4hmCrfERuqdNu1shsj140F4uS4h8orBULhlXQJ0F3sT4lnCuWe32rwxAa8xPutb99jKpHcsxM76TaFzFgQTA==",
"dev": true,
- "requires": {
- "dot-prop": "^4.1.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^1.0.0",
- "unique-string": "^1.0.0",
- "write-file-atomic": "^2.0.0",
- "xdg-basedir": "^3.0.0"
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "tree-dump": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "confusing-browser-globals": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
- "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
- "dev": true
- },
- "connect-history-api-fallback": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
- "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
- "dev": true
- },
- "console-browserify": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
- "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
- "dev": true
- },
- "constants-browserify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
- "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
- "dev": true
- },
- "contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
- "dev": true
- },
- "content-disposition": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
- "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "node_modules/@jsonjoy.com/fs-snapshot": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.56.2.tgz",
+ "integrity": "sha512-Qh0lc8Ujnb2b1D4RQ7CD+BOzqzw2aUpJPIK9SDv+y9LTy3lZ/ydPU7m6qBIH2ePhBKZuBIyVwxOWSvHRaasETQ==",
"dev": true,
- "requires": {
- "safe-buffer": "5.1.2"
- },
+ "license": "Apache-2.0",
"dependencies": {
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- }
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "@jsonjoy.com/json-pack": "^17.65.0",
+ "@jsonjoy.com/util": "^17.65.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
- "dev": true
- },
- "convert-hrtime": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-3.0.0.tgz",
- "integrity": "sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==",
- "dev": true
- },
- "convert-source-map": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
- "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.65.0.tgz",
+ "integrity": "sha512-Xrh7Fm/M0QAYpekSgmskdZYnFdSGnsxJ/tHaolA4bNwWdG9i65S8m83Meh7FOxyJyQAdo4d4J97NOomBLEfkDQ==",
"dev": true,
- "requires": {
- "safe-buffer": "~5.1.1"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "cookie": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
- "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/buffers": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.65.0.tgz",
+ "integrity": "sha512-eBrIXd0/Ld3p9lpDDlMaMn6IEfWqtHMD+z61u0JrIiPzsV1r7m6xDZFRxJyvIFTEO+SWdYF9EiQbXZGd8BzPfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
- "dev": true
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.65.0.tgz",
+ "integrity": "sha512-7MXcRYe7n3BG+fo3jicvjB0+6ypl2Y/bQp79Sp7KeSiiCgLqw4Oled6chVv07/xLVTdo3qa1CD0VCCnPaw+RGA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "cookies": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz",
- "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==",
- "requires": {
- "depd": "~1.1.2",
- "keygrip": "~1.0.3"
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.65.0.tgz",
+ "integrity": "sha512-e0SG/6qUCnVhHa0rjDJHgnXnbsacooHVqQHxspjvlYQSkHm+66wkHw6Gql+3u/WxI/b1VsOdUi0M+fOtkgKGdQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/base64": "17.65.0",
+ "@jsonjoy.com/buffers": "17.65.0",
+ "@jsonjoy.com/codegen": "17.65.0",
+ "@jsonjoy.com/json-pointer": "17.65.0",
+ "@jsonjoy.com/util": "17.65.0",
+ "hyperdyperid": "^1.2.0",
+ "thingies": "^2.5.0",
+ "tree-dump": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "copy-concurrently": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
- "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.65.0.tgz",
+ "integrity": "sha512-uhTe+XhlIZpWOxgPcnO+iSCDgKKBpwkDVTyYiXX9VayGV8HSFVJM67M6pUE71zdnXF1W0Da21AvnhlmdwYPpow==",
"dev": true,
- "requires": {
- "aproba": "^1.1.1",
- "fs-write-stream-atomic": "^1.0.8",
- "iferr": "^0.1.5",
- "mkdirp": "^0.5.1",
- "rimraf": "^2.5.4",
- "run-queue": "^1.0.0"
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/util": "17.65.0"
+ },
+ "engines": {
+ "node": ">=10.0"
},
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": {
+ "version": "17.65.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.65.0.tgz",
+ "integrity": "sha512-cWiEHZccQORf96q2y6zU3wDeIVPeidmGqd9cNKJRYoVHTV0S1eHPy5JTbHpMnGfDvtvujQwQozOqgO9ABu6h0w==",
+ "dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
+ "@jsonjoy.com/buffers": "17.65.0",
+ "@jsonjoy.com/codegen": "17.65.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
- "dev": true
+ "node_modules/@jsonjoy.com/json-pack": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz",
+ "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/base64": "^1.1.2",
+ "@jsonjoy.com/buffers": "^1.2.0",
+ "@jsonjoy.com/codegen": "^1.0.0",
+ "@jsonjoy.com/json-pointer": "^1.0.2",
+ "@jsonjoy.com/util": "^1.9.0",
+ "hyperdyperid": "^1.2.0",
+ "thingies": "^2.5.0",
+ "tree-dump": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "copyfiles": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.2.0.tgz",
- "integrity": "sha512-iJbHJI+8OKqsq+4JF0rqgRkZzo++jqO6Wf4FUU1JM41cJF6JcY5968XyF4tm3Kkm7ZOMrqlljdm8N9oyY5raGw==",
- "dev": true,
- "requires": {
- "glob": "^7.0.5",
- "minimatch": "^3.0.3",
- "mkdirp": "^0.5.1",
- "noms": "0.0.0",
- "through2": "^2.0.1",
- "yargs": "^13.2.4"
- }
- },
- "core-js": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.1.tgz",
- "integrity": "sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg==",
- "dev": true
+ "node_modules/@jsonjoy.com/json-pointer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz",
+ "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/codegen": "^1.0.0",
+ "@jsonjoy.com/util": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
},
- "core-js-compat": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz",
- "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==",
- "dev": true,
- "requires": {
- "browserslist": "^4.8.3",
- "semver": "7.0.0"
- },
- "dependencies": {
- "browserslist": {
- "version": "4.8.6",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz",
- "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001023",
- "electron-to-chromium": "^1.3.341",
- "node-releases": "^1.1.47"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001023",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
- "integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==",
- "dev": true
- },
- "electron-to-chromium": {
- "version": "1.3.344",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
- "integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==",
- "dev": true
- },
- "semver": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
- "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
- "dev": true
- }
+ "node_modules/@jsonjoy.com/util": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz",
+ "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/buffers": "^1.0.0",
+ "@jsonjoy.com/codegen": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "core-js-pure": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz",
- "integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==",
- "dev": true
+ "node_modules/@keyv/serialize": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz",
+ "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
+ "license": "MIT"
},
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ "node_modules/@leichtgewicht/ip-codec": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
+ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
+ "dev": true,
+ "license": "MIT"
},
- "cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "node_modules/@mdx-js/mdx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz",
+ "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdx": "^2.0.0",
+ "collapse-white-space": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-build-jsx": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "estree-walker": "^3.0.0",
+ "hast-util-to-estree": "^3.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "markdown-extensions": "^2.0.0",
+ "periscopic": "^3.0.0",
+ "remark-mdx": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "source-map": "^0.7.0",
+ "unified": "^11.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/@mdx-js/mdx/node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@mdx-js/react": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
+ "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==",
"dev": true,
- "requires": {
- "object-assign": "^4",
- "vary": "^1"
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdx": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16",
+ "react": ">=16"
}
},
- "cosmiconfig": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
- "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "node_modules/@monaco-editor/loader": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz",
+ "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==",
"dev": true,
- "requires": {
- "import-fresh": "^2.0.0",
- "is-directory": "^0.3.1",
- "js-yaml": "^3.13.1",
- "parse-json": "^4.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
- "dev": true,
- "requires": {
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1"
- }
- }
+ "state-local": "^1.0.6"
}
},
- "create-ecdh": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
- "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "node_modules/@monaco-editor/react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
+ "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
"dev": true,
- "requires": {
- "bn.js": "^4.1.0",
- "elliptic": "^6.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@monaco-editor/loader": "^1.5.0"
+ },
+ "peerDependencies": {
+ "monaco-editor": ">= 0.25.0 < 1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "create-error-class": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
- "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
- "requires": {
- "capture-stack-trace": "^1.0.0"
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.1.tgz",
+ "integrity": "sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^0.45.0",
+ "@emnapi/runtime": "^0.45.0",
+ "@tybys/wasm-util": "^0.8.1"
}
},
- "create-hash": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
- "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "node_modules/@noble/hashes": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
+ "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
"dev": true,
- "requires": {
- "cipher-base": "^1.0.1",
- "inherits": "^2.0.1",
- "md5.js": "^1.3.4",
- "ripemd160": "^2.0.1",
- "sha.js": "^2.4.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
}
},
- "create-hmac": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
- "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "node_modules/@nodable/entities": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz",
+ "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@node-rs/jieba": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba/-/jieba-1.9.2.tgz",
+ "integrity": "sha512-H7/Pv9RBEgzcxVAM4yg6L4G10ZoiqVnNcUCs01yV9XIRwLmShUkdthkTqG8heyx2dAMRua+kofd28JtDWBHMfA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@node-rs/jieba-android-arm-eabi": "1.9.2",
+ "@node-rs/jieba-android-arm64": "1.9.2",
+ "@node-rs/jieba-darwin-arm64": "1.9.2",
+ "@node-rs/jieba-darwin-x64": "1.9.2",
+ "@node-rs/jieba-freebsd-x64": "1.9.2",
+ "@node-rs/jieba-linux-arm-gnueabihf": "1.9.2",
+ "@node-rs/jieba-linux-arm64-gnu": "1.9.2",
+ "@node-rs/jieba-linux-arm64-musl": "1.9.2",
+ "@node-rs/jieba-linux-x64-gnu": "1.9.2",
+ "@node-rs/jieba-linux-x64-musl": "1.9.2",
+ "@node-rs/jieba-wasm32-wasi": "1.9.2",
+ "@node-rs/jieba-win32-arm64-msvc": "1.9.2",
+ "@node-rs/jieba-win32-ia32-msvc": "1.9.2",
+ "@node-rs/jieba-win32-x64-msvc": "1.9.2"
+ }
+ },
+ "node_modules/@node-rs/jieba-android-arm-eabi": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-1.9.2.tgz",
+ "integrity": "sha512-FbgUDCvek/KI4mJe5wqbbJi9kDE788YVvsA7DLTE0up+Tb/A7pNIJXq4Pg7zhsgHTVL7EfHHsyd89k+YoSU7Wg==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
- "requires": {
- "cipher-base": "^1.0.3",
- "create-hash": "^1.1.0",
- "inherits": "^2.0.1",
- "ripemd160": "^2.0.0",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "create-react-context": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz",
- "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==",
+ "node_modules/@node-rs/jieba-android-arm64": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm64/-/jieba-android-arm64-1.9.2.tgz",
+ "integrity": "sha512-o6cZz5APAUVBCTG9tNK3XEcLunjGo7Oon1N8+1EHOHPlx4Twzhn1msBtQ+VCgboHUOI+QgZzlY/sTXGfh8IuIA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "requires": {
- "gud": "^1.0.0",
- "warning": "^4.0.3"
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "cross-env": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
- "integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
- "requires": {
- "cross-spawn": "^7.0.0"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.0.tgz",
- "integrity": "sha512-6U/8SMK2FBNnB21oQ4+6Nsodxanw1gTkntYA2zBdkFYFu3ZDx65P2ONEXGSvob/QS6REjVHQ9zxzdOafwFdstw==",
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "path-key": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz",
- "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg=="
- }
+ "node_modules/@node-rs/jieba-darwin-arm64": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.9.2.tgz",
+ "integrity": "sha512-68Pk5phmgn/k5w6nzZWYkDHwJ5wYagprVaSf/WgcPVucw9kAzVMZasGxXo2+Kqn2kMWhD7Dm2NT1TAromQq8Eg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "cross-fetch": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.2.tgz",
- "integrity": "sha1-pH/09/xxLauo9qaVoRyUhEDUVyM=",
+ "node_modules/@node-rs/jieba-darwin-x64": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-1.9.2.tgz",
+ "integrity": "sha512-TXms+q4l7/0a7T+O9t5fQ0m6Qi+4XKgtaRmg089iYbvSp4JfX+XIg+ZLJG+9uc25oWpOgpt+z4bsz9rrHnW/6Q==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "requires": {
- "node-fetch": "2.1.2",
- "whatwg-fetch": "2.0.4"
- },
- "dependencies": {
- "node-fetch": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
- "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=",
- "dev": true
- }
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "cross-spawn": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
- "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
+ "node_modules/@node-rs/jieba-freebsd-x64": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-1.9.2.tgz",
+ "integrity": "sha512-U4p8PSm+1onheBJUKlEyP4VRE42UrH+gXDYMJlWiJDqriAjZLvA+MdF5xiqeppH0dWc/tQu/ECLoj1WXkJKfvg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "which": "^1.2.9"
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "crypt": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
- "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=",
- "dev": true
- },
- "crypto-browserify": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
- "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
- "dev": true,
- "requires": {
- "browserify-cipher": "^1.0.0",
- "browserify-sign": "^4.0.0",
- "create-ecdh": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.0",
- "diffie-hellman": "^5.0.0",
- "inherits": "^2.0.1",
- "pbkdf2": "^3.0.3",
- "public-encrypt": "^4.0.0",
- "randombytes": "^2.0.0",
- "randomfill": "^1.0.3"
+ "node_modules/@node-rs/jieba-linux-arm-gnueabihf": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-1.9.2.tgz",
+ "integrity": "sha512-pelyUy0uFzR7dsnleN8A+4yiNRIRQYFDufbOapP9qkgpEOSvJaD6zYOi3HDm4GdXD5jD0UFNEYMCbi2+Ed2zIg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "crypto-random-string": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
- "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
- "dev": true
- },
- "css-color-keywords": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
- "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=",
- "dev": true
- },
- "css-color-names": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
- "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
- "dev": true
- },
- "css-declaration-sorter": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
- "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
+ "node_modules/@node-rs/jieba-linux-arm64-gnu": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-1.9.2.tgz",
+ "integrity": "sha512-qgrEyYbzXyqoIcl2iLXMvIv4PC1Z84O5TGCGKwGibblHAOpWIVQzkquDhty/aE9Ju1OSJiNZGtE9ISlJXB/PJA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "requires": {
- "postcss": "^7.0.1",
- "timsort": "^0.3.0"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "css-loader": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz",
- "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "css-selector-tokenizer": "^0.7.0",
- "icss-utils": "^2.1.0",
- "loader-utils": "^1.0.2",
- "lodash": "^4.17.11",
- "postcss": "^6.0.23",
- "postcss-modules-extract-imports": "^1.2.0",
- "postcss-modules-local-by-default": "^1.2.0",
- "postcss-modules-scope": "^1.1.0",
- "postcss-modules-values": "^1.3.0",
- "postcss-value-parser": "^3.3.0",
- "source-list-map": "^2.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
- },
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "node_modules/@node-rs/jieba-linux-arm64-musl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-1.9.2.tgz",
+ "integrity": "sha512-FMl9EYCJSKCfJb8rrk+9mmi44SwDyNCMLvxMzG/va59D0BRItmm4EP9Zd2QoKnGawV6Bw4BAXIrNhSg9gWedxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "css-select": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
- "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
+ "node_modules/@node-rs/jieba-linux-x64-gnu": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-1.9.2.tgz",
+ "integrity": "sha512-+7wcz5+3HzOH8+PbNwOPuelTo6ik5jgrz8SFbd+JL6sZLap2pjpKWol5nJyNYNv90yRq1A6p8TrhFXCtVZujFA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "requires": {
- "boolbase": "~1.0.0",
- "css-what": "2.1",
- "domutils": "1.5.1",
- "nth-check": "~1.0.1"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "css-select-base-adapter": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz",
- "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=",
- "dev": true
- },
- "css-selector-tokenizer": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz",
- "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==",
+ "node_modules/@node-rs/jieba-linux-x64-musl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-1.9.2.tgz",
+ "integrity": "sha512-ims1jOq99zTvcwcUXKuD+WT36KHHVNTq9Fm663YzMMBO+5sqLleQtQkZgbh0BHJI25jvW9IrBCvuS1ZwKZ6kOA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "requires": {
- "cssesc": "^3.0.0",
- "fastparse": "^1.1.2",
- "regexpu-core": "^4.6.0"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "css-to-react-native": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
- "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+ "node_modules/@node-rs/jieba-wasm32-wasi": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-wasm32-wasi/-/jieba-wasm32-wasi-1.9.2.tgz",
+ "integrity": "sha512-h/lWDJCYlsH3VUNWBCO2exite7VDPgjpOmaQgTQ6aNI5x8rR3NEkyXfmy/yWIev63tdMr9nD3sXw38QQp4ddIA==",
+ "cpu": [
+ "wasm32"
+ ],
"dev": true,
- "requires": {
- "camelize": "^1.0.0",
- "css-color-keywords": "^1.0.0",
- "postcss-value-parser": "^4.0.2"
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
}
},
- "css-tree": {
- "version": "1.0.0-alpha.28",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz",
- "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==",
+ "node_modules/@node-rs/jieba-win32-arm64-msvc": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-1.9.2.tgz",
+ "integrity": "sha512-a3BkMcvW9sedSA1XlYvGEQSNQQrK4kUMULKTXmqboLjFbwI9v6flpDCiJyw2pXYs+1WT6XjTHvEyVsoGCVMRlQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "requires": {
- "mdn-data": "~1.1.0",
- "source-map": "^0.5.3"
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "css-url-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz",
- "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=",
- "dev": true
+ "node_modules/@node-rs/jieba-win32-ia32-msvc": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-1.9.2.tgz",
+ "integrity": "sha512-k8xPkEOMJM3ic4UzvmvKELjPQsIFSo7mw1wx4tNrUN5mu5ANIhk1Fq0zXOcTbpIe7jHPsWaB5jYtBeeJ0h09iA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
},
- "css-what": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
- "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=",
- "dev": true
+ "node_modules/@node-rs/jieba-win32-x64-msvc": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-1.9.2.tgz",
+ "integrity": "sha512-wIseuWUK+WdikhMFPLvr80D0I4wQJSdFsRIljiUhKwnNQESG20zIxSYE1qy7BNBd7s8fAn1BC6PGeg7S/2KliQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
},
- "cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "cssnano": {
- "version": "4.1.10",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz",
- "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==",
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
- "requires": {
- "cosmiconfig": "^5.0.0",
- "cssnano-preset-default": "^4.0.7",
- "is-resolvable": "^1.0.0",
- "postcss": "^7.0.0"
+ "engines": {
+ "node": ">= 8"
}
},
- "cssnano-preset-default": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz",
- "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==",
- "dev": true,
- "requires": {
- "css-declaration-sorter": "^4.0.1",
- "cssnano-util-raw-cache": "^4.0.1",
- "postcss": "^7.0.0",
- "postcss-calc": "^7.0.1",
- "postcss-colormin": "^4.0.3",
- "postcss-convert-values": "^4.0.1",
- "postcss-discard-comments": "^4.0.2",
- "postcss-discard-duplicates": "^4.0.2",
- "postcss-discard-empty": "^4.0.1",
- "postcss-discard-overridden": "^4.0.1",
- "postcss-merge-longhand": "^4.0.11",
- "postcss-merge-rules": "^4.0.3",
- "postcss-minify-font-values": "^4.0.2",
- "postcss-minify-gradients": "^4.0.2",
- "postcss-minify-params": "^4.0.2",
- "postcss-minify-selectors": "^4.0.2",
- "postcss-normalize-charset": "^4.0.1",
- "postcss-normalize-display-values": "^4.0.2",
- "postcss-normalize-positions": "^4.0.2",
- "postcss-normalize-repeat-style": "^4.0.2",
- "postcss-normalize-string": "^4.0.2",
- "postcss-normalize-timing-functions": "^4.0.2",
- "postcss-normalize-unicode": "^4.0.1",
- "postcss-normalize-url": "^4.0.1",
- "postcss-normalize-whitespace": "^4.0.2",
- "postcss-ordered-values": "^4.1.2",
- "postcss-reduce-initial": "^4.0.3",
- "postcss-reduce-transforms": "^4.0.2",
- "postcss-svgo": "^4.0.2",
- "postcss-unique-selectors": "^4.0.1"
- }
- },
- "cssnano-util-get-arguments": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
- "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=",
- "dev": true
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "cssnano-util-get-match": {
+ "node_modules/@octokit/auth-token": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
- "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=",
- "dev": true
- },
- "cssnano-util-raw-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz",
- "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
}
},
- "cssnano-util-same-parent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz",
- "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==",
- "dev": true
+ "node_modules/@octokit/core": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
+ "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/auth-token": "^4.0.0",
+ "@octokit/graphql": "^7.1.0",
+ "@octokit/request": "^8.4.1",
+ "@octokit/request-error": "^5.1.1",
+ "@octokit/types": "^13.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
},
- "csso": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz",
- "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==",
+ "node_modules/@octokit/endpoint": {
+ "version": "9.0.6",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+ "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
"dev": true,
- "requires": {
- "css-tree": "1.0.0-alpha.29"
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
},
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
+ "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "css-tree": {
- "version": "1.0.0-alpha.29",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz",
- "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==",
- "dev": true,
- "requires": {
- "mdn-data": "~1.1.0",
- "source-map": "^0.5.3"
- }
- }
+ "@octokit/request": "^8.4.1",
+ "@octokit/types": "^13.0.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
}
},
- "csstype": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.0.tgz",
- "integrity": "sha512-by8hi8BlLbowQq0qtkx54d9aN73R9oUW20HISpka5kmgsR9F7nnxgfsemuR2sdCKZh+CDNf5egW9UZMm4mgJRg==",
- "dev": true
+ "node_modules/@octokit/openapi-types": {
+ "version": "24.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+ "dev": true,
+ "license": "MIT"
},
- "currently-unhandled": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "node_modules/@octokit/plugin-paginate-rest": {
+ "version": "11.4.4-cjs.2",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz",
+ "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==",
"dev": true,
- "requires": {
- "array-find-index": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.7.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": "5"
}
},
- "cyclist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
- "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
- "dev": true
- },
- "cypress": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/cypress/-/cypress-4.4.1.tgz",
- "integrity": "sha512-LcskZ/PXRG9XTlEeeenKqz/KddT1x+7O7dqXsdKWPII01LxLNmNHIvHnlUqApchVbinJ5vir6J255CkELSeL0A==",
- "dev": true,
- "requires": {
- "@cypress/listr-verbose-renderer": "0.4.1",
- "@cypress/request": "2.88.5",
- "@cypress/xvfb": "1.2.4",
- "@types/blob-util": "1.3.3",
- "@types/bluebird": "3.5.29",
- "@types/chai": "4.2.7",
- "@types/chai-jquery": "1.1.40",
- "@types/jquery": "3.3.31",
- "@types/lodash": "4.14.149",
- "@types/minimatch": "3.0.3",
- "@types/mocha": "5.2.7",
- "@types/sinon": "7.5.1",
- "@types/sinon-chai": "3.2.3",
- "@types/sizzle": "2.3.2",
- "arch": "2.1.1",
- "bluebird": "3.7.2",
- "cachedir": "2.3.0",
- "chalk": "2.4.2",
- "check-more-types": "2.24.0",
- "cli-table3": "0.5.1",
- "commander": "4.1.0",
- "common-tags": "1.8.0",
- "debug": "4.1.1",
- "eventemitter2": "4.1.2",
- "execa": "1.0.0",
- "executable": "4.1.1",
- "extract-zip": "1.7.0",
- "fs-extra": "8.1.0",
- "getos": "3.1.4",
- "is-ci": "2.0.0",
- "is-installed-globally": "0.1.0",
- "lazy-ass": "1.6.0",
- "listr": "0.14.3",
- "lodash": "4.17.15",
- "log-symbols": "3.0.0",
- "minimist": "1.2.5",
- "moment": "2.24.0",
- "ospath": "1.2.2",
- "pretty-bytes": "5.3.0",
- "ramda": "0.26.1",
- "request-progress": "3.0.0",
- "supports-color": "7.1.0",
- "tmp": "0.1.0",
- "untildify": "4.0.0",
- "url": "0.11.0",
- "yauzl": "2.10.0"
- },
- "dependencies": {
- "@types/chai": {
- "version": "4.2.7",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz",
- "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==",
- "dev": true
- },
- "@types/lodash": {
- "version": "4.14.149",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
- "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
- "dev": true
- },
- "@types/mocha": {
- "version": "5.2.7",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
- "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "commander": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz",
- "integrity": "sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==",
- "dev": true
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "log-symbols": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- },
- "dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- }
- }
- }
- }
- },
- "damerau-levenshtein": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
- "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==",
- "dev": true
- },
- "danger": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/danger/-/danger-10.1.1.tgz",
- "integrity": "sha512-kRJW7TrVYe3nzTILRahXuEvG/+EZwyHrDXTTHA6nkd+4Ga+F4A4AFNkT7d0hEi7OlhhVx8W9BkxNcSO1xEJrAA==",
+ "node_modules/@octokit/plugin-request-log": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
+ "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
"dev": true,
- "requires": {
- "@babel/polyfill": "^7.2.5",
- "@octokit/rest": "^16.43.1",
- "async-retry": "1.2.3",
- "chalk": "^2.3.0",
- "commander": "^2.18.0",
- "debug": "^4.1.1",
- "fast-json-patch": "^3.0.0-1",
- "get-stdin": "^6.0.0",
- "gitlab": "^10.0.1",
- "http-proxy-agent": "^2.1.0",
- "https-proxy-agent": "^2.2.1",
- "hyperlinker": "^1.0.0",
- "json5": "^2.1.0",
- "jsonpointer": "^4.0.1",
- "jsonwebtoken": "^8.4.0",
- "lodash.find": "^4.6.0",
- "lodash.includes": "^4.3.0",
- "lodash.isobject": "^3.0.2",
- "lodash.keys": "^4.0.8",
- "lodash.mapvalues": "^4.6.0",
- "lodash.memoize": "^4.1.2",
- "memfs-or-file-map-to-github-branch": "^1.1.0",
- "micromatch": "^3.1.10",
- "node-cleanup": "^2.1.2",
- "node-fetch": "^2.3.0",
- "override-require": "^1.1.1",
- "p-limit": "^2.1.0",
- "parse-diff": "^0.7.0",
- "parse-git-config": "^2.0.3",
- "parse-github-url": "^1.0.2",
- "parse-link-header": "^1.0.1",
- "pinpoint": "^1.1.0",
- "prettyjson": "^1.2.1",
- "readline-sync": "^1.4.9",
- "require-from-string": "^2.0.2",
- "supports-hyperlinks": "^1.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
},
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "json5": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
- "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- }
+ "peerDependencies": {
+ "@octokit/core": "5"
}
},
- "danger-plugin-no-test-shortcuts": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/danger-plugin-no-test-shortcuts/-/danger-plugin-no-test-shortcuts-2.0.0.tgz",
- "integrity": "sha1-Atu3JFWBlFTTbNWVLi/w9H34ENo=",
+ "node_modules/@octokit/plugin-rest-endpoint-methods": {
+ "version": "13.3.2-cjs.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz",
+ "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==",
"dev": true,
- "requires": {
- "lodash": "^4.17.4"
- }
- },
- "dashdash": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
- "requires": {
- "assert-plus": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.8.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": "^5"
}
},
- "date-fns": {
- "version": "1.29.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
- "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
- "dev": true
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
+ "node_modules/@octokit/request": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+ "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/endpoint": "^9.0.6",
+ "@octokit/request-error": "^5.1.1",
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
}
},
- "decamelize": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz",
- "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==",
- "requires": {
- "xregexp": "^4.2.4"
+ "node_modules/@octokit/request-error": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+ "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.1.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">= 18"
}
},
- "decamelize-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
- "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=",
+ "node_modules/@octokit/rest": {
+ "version": "20.1.2",
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz",
+ "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==",
"dev": true,
- "requires": {
- "decamelize": "^1.1.0",
- "map-obj": "^1.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
- "map-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
- "dev": true
- }
+ "@octokit/core": "^5.0.2",
+ "@octokit/plugin-paginate-rest": "11.4.4-cjs.2",
+ "@octokit/plugin-request-log": "^4.0.0",
+ "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1"
+ },
+ "engines": {
+ "node": ">= 18"
}
},
- "decode-uri-component": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
- "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
- },
- "decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+ "node_modules/@octokit/types": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"dev": true,
- "requires": {
- "mimic-response": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^24.2.0"
}
},
- "dedent": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
- "dev": true
- },
- "deep-eql": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
- "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
- "requires": {
- "type-detect": "^4.0.0"
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "engines": {
+ "node": ">=8.0.0"
}
},
- "deep-equal": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
- "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
- "dev": true,
- "requires": {
- "is-arguments": "^1.0.4",
- "is-date-object": "^1.0.1",
- "is-regex": "^1.0.4",
- "object-is": "^1.0.1",
- "object-keys": "^1.1.1",
- "regexp.prototype.flags": "^1.2.0"
+ "node_modules/@opentelemetry/core": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz",
+ "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
},
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/resources": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz",
+ "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==",
+ "license": "Apache-2.0",
+ "peer": true,
"dependencies": {
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- }
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
}
},
- "deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true
+ "node_modules/@opentelemetry/sdk-trace": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz",
+ "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
},
- "deep-is": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
+ "node_modules/@opentelemetry/sdk-trace-base": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz",
+ "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/core": "2.10.0",
+ "@opentelemetry/resources": "2.10.0",
+ "@opentelemetry/sdk-trace": "2.10.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
},
- "deepmerge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
- "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
- "dev": true
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz",
+ "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
},
- "default-gateway": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
- "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+ "node_modules/@paralleldrive/cuid2": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
+ "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
"dev": true,
- "requires": {
- "execa": "^1.0.0",
- "ip-regex": "^2.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.1.5"
}
},
- "default-require-extensions": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz",
- "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==",
+ "node_modules/@peculiar/asn1-cms": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.0.tgz",
+ "integrity": "sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==",
"dev": true,
- "requires": {
- "strip-bom": "^4.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
- }
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "@peculiar/asn1-x509-attr": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "defer-to-connect": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.1.tgz",
- "integrity": "sha512-2e0FJesseUqQj671gvZWfUyxpnFx/5n4xleamlpCD3U6Fm5dh5qzmmLNxNhtmHF06+SYVHH8QU6FACffYTnj0Q==",
- "dev": true
- },
- "define-properties": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
- "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
- "requires": {
- "foreach": "^2.0.5",
- "object-keys": "^1.0.8"
+ "node_modules/@peculiar/asn1-csr": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.0.tgz",
+ "integrity": "sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "node_modules/@peculiar/asn1-ecc": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.0.tgz",
+ "integrity": "sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw==",
"dev": true,
- "requires": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "del": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz",
- "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==",
+ "node_modules/@peculiar/asn1-pfx": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.0.tgz",
+ "integrity": "sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ==",
"dev": true,
- "requires": {
- "globby": "^10.0.1",
- "graceful-fs": "^4.2.2",
- "is-glob": "^4.0.1",
- "is-path-cwd": "^2.2.0",
- "is-path-inside": "^3.0.1",
- "p-map": "^3.0.0",
- "rimraf": "^3.0.0",
- "slash": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "graceful-fs": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
- "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
- "dev": true
- },
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-path-inside": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.1.tgz",
- "integrity": "sha512-CKstxrctq1kUesU6WhtZDbYKzzYBuRH0UYInAVrkc/EYdB9ltbfE0gOoayG9nhohG6447sOOVGhHqsdmBvkbNg==",
- "dev": true
- },
- "p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
- "dev": true,
- "requires": {
- "aggregate-error": "^3.0.0"
- }
- }
+ "@peculiar/asn1-cms": "^2.6.0",
+ "@peculiar/asn1-pkcs8": "^2.6.0",
+ "@peculiar/asn1-rsa": "^2.6.0",
+ "@peculiar/asn1-schema": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
- },
- "denque": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
- "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
- },
- "depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
- },
- "deprecation": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
- "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
- "dev": true
- },
- "des.js": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
- "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+ "node_modules/@peculiar/asn1-pkcs8": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.0.tgz",
+ "integrity": "sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "destroy": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
- "dev": true
- },
- "detect-indent": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
- "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
- "dev": true
- },
- "detect-node": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
- "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw=="
- },
- "detect-port": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz",
- "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==",
+ "node_modules/@peculiar/asn1-pkcs9": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.0.tgz",
+ "integrity": "sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw==",
"dev": true,
- "requires": {
- "address": "^1.0.1",
- "debug": "^2.6.0"
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-cms": "^2.6.0",
+ "@peculiar/asn1-pfx": "^2.6.0",
+ "@peculiar/asn1-pkcs8": "^2.6.0",
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "@peculiar/asn1-x509-attr": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "devcert": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/devcert/-/devcert-1.1.0.tgz",
- "integrity": "sha512-ppyIBJueMMisYvJABaXESY10CwEm1pUXoLOm6TeBO2bbDUQE8ZjJPNADlu31I2InL7hduSgratzRG/dHUDF41w==",
- "dev": true,
- "requires": {
- "@types/configstore": "^2.1.1",
- "@types/debug": "^0.0.30",
- "@types/get-port": "^3.2.0",
- "@types/glob": "^5.0.34",
- "@types/lodash": "^4.14.92",
- "@types/mkdirp": "^0.5.2",
- "@types/node": "^8.5.7",
- "@types/rimraf": "^2.0.2",
- "@types/tmp": "^0.0.33",
- "application-config-path": "^0.1.0",
- "command-exists": "^1.2.4",
- "configstore": "^3.0.0",
- "debug": "^3.1.0",
- "eol": "^0.9.1",
- "get-port": "^3.2.0",
- "glob": "^7.1.2",
- "lodash": "^4.17.4",
- "mkdirp": "^0.5.1",
- "password-prompt": "^1.0.4",
- "rimraf": "^2.6.2",
- "sudo-prompt": "^8.2.0",
- "tmp": "^0.0.33",
- "tslib": "^1.10.0"
- },
- "dependencies": {
- "@types/glob": {
- "version": "5.0.36",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.36.tgz",
- "integrity": "sha512-KEzSKuP2+3oOjYYjujue6Z3Yqis5HKA1BsIC+jZ1v3lrRNdsqyNNtX0rQf6LSuI4DJJ2z5UV//zBZCcvM0xikg==",
- "dev": true,
- "requires": {
- "@types/events": "*",
- "@types/minimatch": "*",
- "@types/node": "*"
- }
- },
- "@types/node": {
- "version": "8.10.59",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz",
- "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==",
- "dev": true
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.2"
- }
- },
- "tslib": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
- "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
- "dev": true
- }
+ "node_modules/@peculiar/asn1-rsa": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.0.tgz",
+ "integrity": "sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
- "dev": true
- },
- "diffie-hellman": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
- "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "node_modules/@peculiar/asn1-schema": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz",
+ "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==",
"dev": true,
- "requires": {
- "bn.js": "^4.1.0",
- "miller-rabin": "^4.0.0",
- "randombytes": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "asn1js": "^3.0.6",
+ "pvtsutils": "^1.3.6",
+ "tslib": "^2.8.1"
}
},
- "dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "node_modules/@peculiar/asn1-x509": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.0.tgz",
+ "integrity": "sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA==",
"dev": true,
- "requires": {
- "path-type": "^4.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- }
+ "@peculiar/asn1-schema": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "pvtsutils": "^1.3.6",
+ "tslib": "^2.8.1"
}
},
- "discontinuous-range": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
- "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
- "dev": true
- },
- "disparity": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/disparity/-/disparity-3.0.0.tgz",
- "integrity": "sha512-n94Rzbv2ambRaFzrnBf34IEiyOdIci7maRpMkoQWB6xFYGA7Nbs0Z5YQzMfTeyQeelv23nayqOcssBoc6rKrgw==",
+ "node_modules/@peculiar/asn1-x509-attr": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.0.tgz",
+ "integrity": "sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA==",
"dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "diff": "^4.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
- "dev": true,
- "requires": {
- "@types/color-name": "^1.1.1",
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- }
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "asn1js": "^3.0.6",
+ "tslib": "^2.8.1"
}
},
- "dns-equal": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
- "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
- "dev": true
- },
- "dns-packet": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
- "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+ "node_modules/@peculiar/x509": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
+ "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
"dev": true,
- "requires": {
- "ip": "^1.1.0",
- "safe-buffer": "^5.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-cms": "^2.6.0",
+ "@peculiar/asn1-csr": "^2.6.0",
+ "@peculiar/asn1-ecc": "^2.6.0",
+ "@peculiar/asn1-pkcs9": "^2.6.0",
+ "@peculiar/asn1-rsa": "^2.6.0",
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "pvtsutils": "^1.3.6",
+ "reflect-metadata": "^0.2.2",
+ "tslib": "^2.8.1",
+ "tsyringe": "^4.10.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
}
},
- "dns-txt": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
- "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
- "requires": {
- "buffer-indexof": "^1.0.0"
+ "optional": true,
+ "engines": {
+ "node": ">=14"
}
},
- "doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "node_modules/@pkgr/core": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz",
+ "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==",
"dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
},
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- }
+ "funding": {
+ "url": "https://opencollective.com/pkgr"
}
},
- "dom-converter": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
- "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "node_modules/@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
"dev": true,
- "requires": {
- "utila": "~0.4"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22.0"
}
},
- "dom-helpers": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
- "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "node_modules/@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.1.2"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
- "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.12.0"
- }
- }
+ "graceful-fs": "4.2.10"
+ },
+ "engines": {
+ "node": ">=12.22.0"
}
},
- "dom-serializer": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
- "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+ "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true,
- "requires": {
- "domelementtype": "~1.1.1",
- "entities": "~1.1.1"
- },
+ "license": "ISC"
+ },
+ "node_modules/@pnpm/npm-conf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz",
+ "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "domelementtype": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
- "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
- "dev": true
- }
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "dom-walk": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
- "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=",
- "dev": true
- },
- "domain-browser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
- "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
- "dev": true
- },
- "domelementtype": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
- "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
- "dev": true
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.29",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+ "dev": true,
+ "license": "MIT"
},
- "domhandler": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
- "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
+ "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==",
"dev": true,
- "requires": {
- "domelementtype": "1"
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@standard-schema/utils": "^0.3.0",
+ "immer": "^11.0.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
}
},
- "domutils": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
- "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "node_modules/@reduxjs/toolkit/node_modules/immer": {
+ "version": "11.1.3",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz",
+ "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==",
"dev": true,
- "requires": {
- "dom-serializer": "0",
- "domelementtype": "1"
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
}
},
- "dot-prop": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
- "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+ "node_modules/@reduxjs/toolkit/node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"dev": true,
- "requires": {
- "is-obj": "^1.0.0"
+ "license": "MIT",
+ "peerDependencies": {
+ "redux": "^5.0.0"
}
},
- "dotenv": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
- "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
+ "node_modules/@renovatebot/pep440": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@renovatebot/pep440/-/pep440-5.0.0.tgz",
+ "integrity": "sha512-91cFM32/pnOAvC7W1W1kPPI9QM2y6OrYCeomz7pop4A+3Ksh2XnZXGznUMlzR7YbOQlg7X/MNlF27s9Yt9hhTA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^22.11.0 || >=24.10.0",
+ "pnpm": ">=10.0.0"
+ }
},
- "duplexer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
- "dev": true
+ "node_modules/@renovatebot/ruby-semver": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@renovatebot/ruby-semver/-/ruby-semver-5.0.0.tgz",
+ "integrity": "sha512-yQgeq4HVV9mh2AJHHlStd//7mD/96lWj8KWB5Szi5YDjPwpvk12Sj3H+Y4waNC3N2D4t5xqbwYYpTHB5KsrSVg==",
+ "license": "MIT",
+ "engines": {
+ "node": "^22.11.0 || >=24.10.0",
+ "pnpm": ">=10.0.0"
+ }
},
- "duplexer3": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
- "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
"dev": true
},
- "duplexify": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
- "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.0.0",
- "inherits": "^2.0.1",
- "readable-stream": "^2.0.0",
- "stream-shift": "^1.0.0"
+ "node_modules/@sec-ant/readable-stream": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+ "license": "MIT"
+ },
+ "node_modules/@sentry/conventions": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz",
+ "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@sentry/core": {
+ "version": "10.67.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.67.0.tgz",
+ "integrity": "sha512-b6U3pJ8AUvN9aouq0vl+VZI8KT8RslBsfGMFuNwRr313zOmdmFJBZqTiUw9VGgJ2jGKxLO9alm9rlxBfX4hf+w==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/conventions": "^0.16.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@sentry/node-core": {
+ "version": "10.67.0",
+ "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.67.0.tgz",
+ "integrity": "sha512-dBHHRwZyan1pOnFJ+sNBvR8TkXbZAfZU/jpxmALS3JZ2/8AGR7cQKL+b7SleKuJ7iUDZyklN3Nqi0i5JkcA+HA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/conventions": "^0.16.0",
+ "@sentry/core": "10.67.0",
+ "@sentry/opentelemetry": "10.67.0",
+ "import-in-the-middle": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/core": "^1.30.1 || ^2.1.0",
+ "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1",
+ "@opentelemetry/instrumentation": ">=0.57.1 <1",
+ "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@opentelemetry/core": {
+ "optional": true
+ },
+ "@opentelemetry/exporter-trace-otlp-http": {
+ "optional": true
+ },
+ "@opentelemetry/instrumentation": {
+ "optional": true
+ },
+ "@opentelemetry/sdk-trace-base": {
+ "optional": true
+ }
}
},
- "ecc-jsbn": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
- "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
- "requires": {
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.1.0"
+ "node_modules/@sentry/opentelemetry": {
+ "version": "10.67.0",
+ "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.67.0.tgz",
+ "integrity": "sha512-oLTOrAK1rOqmYRktOJZwz37B1seXPx1W2FTMVtzTVNjMFA/LZwGzePeZzhUOgzZgfLHixMd/ceWtGqoxAndcjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/conventions": "^0.16.0",
+ "@sentry/core": "10.67.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/core": "^1.30.1 || ^2.1.0",
+ "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
}
},
- "ecdsa-sig-formatter": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
- "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "node_modules/@shields_io/camp": {
+ "version": "18.1.2",
+ "resolved": "https://registry.npmjs.org/@shields_io/camp/-/camp-18.1.2.tgz",
+ "integrity": "sha512-zpc3QItUK9l/5kef2w9cNNt07u0+Gz8QaR/Ye6AWcuOe8TI1T+81M0Z7OIqY6FpQ3rGAmpxJ/H/FLomD3UvABQ==",
+ "dependencies": {
+ "cookies": "~0.7.1",
+ "fleau": "~16.2.0",
+ "formidable": "~1.2.0",
+ "handle-thing": "^2.0.1",
+ "multilog": "~14.11.22",
+ "socket.io": "^2.0.4",
+ "spdy": "^4.0.2",
+ "ws": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=0.11.8"
+ }
+ },
+ "node_modules/@sideway/address": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
+ "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
"dev": true,
- "requires": {
- "safe-buffer": "^5.0.1"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0"
}
},
- "ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
- "dev": true
+ "node_modules/@sideway/formula": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
+ "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
+ "dev": true,
+ "license": "BSD-3-Clause"
},
- "electron-to-chromium": {
- "version": "1.3.376",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.376.tgz",
- "integrity": "sha512-cv/PYVz5szeMz192ngilmezyPNFkUjuynuL2vNdiqIrio440nfTDdc0JJU0TS2KHLSVCs9gBbt4CFqM+HcBnjw==",
- "dev": true
+ "node_modules/@sideway/pinpoint": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
+ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
},
- "elegant-spinner": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
- "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
"dev": true
},
- "elliptic": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
- "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
+ "node_modules/@sindresorhus/base62": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz",
+ "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==",
"dev": true,
- "requires": {
- "bn.js": "^4.4.0",
- "brorand": "^1.0.1",
- "hash.js": "^1.0.0",
- "hmac-drbg": "^1.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
- "dev": true
- },
- "emojic": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/emojic/-/emojic-1.1.15.tgz",
- "integrity": "sha512-Rm2fQkSfPmCI9Gf876L4LVxvmBzTp+F5MPo9pxkJlXaU0MAOOeT0bPcqX3NMugMI80IdYlthfeMMBWfU+/54LQ==",
- "requires": {
- "camelo": "^1.0.0",
- "emojilib": "^2.0.2",
- "iterate-object": "^1.2.0",
- "r-json": "^1.1.0"
+ "node_modules/@sindresorhus/is": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-8.1.0.tgz",
+ "integrity": "sha512-2SX/1jW6CIMAiebvVv5ZInoCEuWQmMyBoJXXGC6Vjakjp/fpxP5eHs7/V6WKuPEIbuK06+VpjH+vjLQhr98rDQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
- "emojilib": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
- "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw=="
- },
- "emojis-list": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
- "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
- "dev": true
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
- "dev": true
- },
- "end-of-stream": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
- "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
"dev": true,
- "requires": {
- "once": "^1.4.0"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "type-detect": "4.0.8"
}
},
- "engine.io": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz",
- "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==",
- "requires": {
- "accepts": "~1.3.4",
- "base64id": "2.0.0",
- "cookie": "0.3.1",
- "debug": "~4.1.0",
- "engine.io-parser": "~2.2.0",
- "ws": "^7.1.2"
- },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "15.4.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz",
+ "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
- "base64id": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
- "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "engine.io-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
- "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
- "requires": {
- "after": "0.8.2",
- "arraybuffer.slice": "~0.0.7",
- "base64-arraybuffer": "0.1.5",
- "blob": "0.0.5",
- "has-binary2": "~1.0.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "ws": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
- "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
- }
+ "@sinonjs/commons": "^3.0.1"
}
},
- "engine.io-client": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
- "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
- "requires": {
- "component-emitter": "1.2.1",
- "component-inherit": "0.0.3",
- "debug": "~4.1.0",
- "engine.io-parser": "~2.2.0",
- "has-cors": "1.1.0",
- "indexof": "0.0.1",
- "parseqs": "0.0.5",
- "parseuri": "0.0.5",
- "ws": "~6.1.0",
- "xmlhttprequest-ssl": "~1.5.4",
- "yeast": "0.1.2"
- },
+ "node_modules/@sinonjs/samsam": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz",
+ "integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "engine.io-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
- "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
- "requires": {
- "after": "0.8.2",
- "arraybuffer.slice": "~0.0.7",
- "base64-arraybuffer": "0.1.5",
- "blob": "0.0.5",
- "has-binary2": "~1.0.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "ws": {
- "version": "6.1.4",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
- "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
- "requires": {
- "async-limiter": "~1.0.0"
- }
- }
+ "@sinonjs/commons": "^3.0.1",
+ "type-detect": "^4.1.0"
}
},
- "enhanced-resolve": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
- "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+ "node_modules/@sinonjs/samsam/node_modules/type-detect": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
+ "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "memory-fs": "^0.5.0",
- "tapable": "^1.0.0"
- },
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@slorber/remark-comment": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz",
+ "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "memory-fs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
- "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
- "dev": true,
- "requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
- }
- }
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.1.0",
+ "micromark-util-symbol": "^1.0.1"
}
},
- "entities": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
- "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
- "dev": true
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "license": "MIT"
},
- "envinfo": {
- "version": "7.5.0",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.0.tgz",
- "integrity": "sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==",
- "dev": true
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
+ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
+ "dev": true,
+ "license": "MIT"
},
- "enzyme": {
- "version": "3.11.0",
- "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz",
- "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==",
- "dev": true,
- "requires": {
- "array.prototype.flat": "^1.2.3",
- "cheerio": "^1.0.0-rc.3",
- "enzyme-shallow-equal": "^1.0.1",
- "function.prototype.name": "^1.1.2",
- "has": "^1.0.3",
- "html-element-map": "^1.2.0",
- "is-boolean-object": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-number-object": "^1.0.4",
- "is-regex": "^1.0.5",
- "is-string": "^1.0.5",
- "is-subset": "^0.1.1",
- "lodash.escape": "^4.0.1",
- "lodash.isequal": "^4.5.0",
- "object-inspect": "^1.7.0",
- "object-is": "^1.0.2",
- "object.assign": "^4.1.0",
- "object.entries": "^1.1.1",
- "object.values": "^1.1.1",
- "raf": "^3.4.1",
- "rst-selector-parser": "^2.2.3",
- "string.prototype.trim": "^1.2.1"
- },
- "dependencies": {
- "array.prototype.flat": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
- "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- }
- },
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "enzyme-shallow-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz",
- "integrity": "sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3",
- "object-is": "^1.0.2"
- }
- },
- "es-abstract": {
- "version": "1.17.0-next.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz",
- "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.4",
- "is-regex": "^1.0.4",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.0",
- "string.prototype.trimright": "^2.1.0"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
- "dev": true
- },
- "object-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
- "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
- "dev": true
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "object.entries": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz",
- "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- }
- }
- },
- "enzyme-adapter-react-16": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz",
- "integrity": "sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q==",
- "dev": true,
- "requires": {
- "enzyme-adapter-utils": "^1.13.0",
- "enzyme-shallow-equal": "^1.0.1",
- "has": "^1.0.3",
- "object.assign": "^4.1.0",
- "object.values": "^1.1.1",
- "prop-types": "^15.7.2",
- "react-is": "^16.12.0",
- "react-test-renderer": "^16.0.0-0",
- "semver": "^5.7.0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
- "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "react-is": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
- "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
- }
- },
- "enzyme-adapter-utils": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz",
- "integrity": "sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ==",
- "dev": true,
- "requires": {
- "airbnb-prop-types": "^2.15.0",
- "function.prototype.name": "^1.1.2",
- "object.assign": "^4.1.0",
- "object.fromentries": "^2.0.2",
- "prop-types": "^15.7.2",
- "semver": "^5.7.1"
- },
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.11.0.tgz",
+ "integrity": "sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw==",
+ "dev": true,
"dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
- "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "object.fromentries": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz",
- "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
+ "@typescript-eslint/utils": "^8.13.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
}
},
- "enzyme-shallow-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz",
- "integrity": "sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3",
- "object-is": "^1.0.2"
- },
- "dependencies": {
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "object-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
- "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
- "dev": true
- }
+ "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "eol": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz",
- "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==",
- "dev": true
- },
- "errno": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
- "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+ "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
- "requires": {
- "prr": "~1.0.1"
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "error-ex": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
- "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
"dev": true,
- "requires": {
- "is-arrayish": "^0.2.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "error-stack-parser": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz",
- "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==",
+ "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
"dev": true,
- "requires": {
- "stackframe": "^1.1.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "es-abstract": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
- "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==",
- "requires": {
- "es-to-primitive": "^1.1.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.1",
- "is-callable": "^1.1.3",
- "is-regex": "^1.0.4"
+ "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
+ "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "es-to-primitive": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
- "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
- "requires": {
- "is-callable": "^1.1.1",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.1"
+ "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
+ "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
- "dev": true
- },
- "es6-promise": {
- "version": "4.2.8",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
- "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
- },
- "es6-promisify": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
- "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
+ "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
"dev": true,
- "requires": {
- "es6-promise": "^4.0.3"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "escape-goat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
- "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
- "dev": true
- },
- "escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
- "dev": true
- },
- "escape-quotes": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/escape-quotes/-/escape-quotes-1.0.2.tgz",
- "integrity": "sha1-tIltSmz4LdWzP0m3E0CMY4D2zZc=",
+ "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
+ "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
"dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
},
- "dependencies": {
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="
+ "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
+ "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
},
- "escodegen": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz",
- "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==",
- "requires": {
- "esprima": "^3.1.3",
- "estraverse": "^4.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.5.6"
+ "node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
+ "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "eslint": {
- "version": "6.8.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
- "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+ "node_modules/@svgr/babel-preset": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
+ "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
"dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "ajv": "^6.10.0",
- "chalk": "^2.1.0",
- "cross-spawn": "^6.0.5",
- "debug": "^4.0.1",
- "doctrine": "^3.0.0",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^1.4.3",
- "eslint-visitor-keys": "^1.1.0",
- "espree": "^6.1.2",
- "esquery": "^1.0.1",
- "esutils": "^2.0.2",
- "file-entry-cache": "^5.0.1",
- "functional-red-black-tree": "^1.0.1",
- "glob-parent": "^5.0.0",
- "globals": "^12.1.0",
- "ignore": "^4.0.6",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "inquirer": "^7.0.0",
- "is-glob": "^4.0.0",
- "js-yaml": "^3.13.1",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.3.0",
- "lodash": "^4.17.14",
- "minimatch": "^3.0.4",
- "mkdirp": "^0.5.1",
- "natural-compare": "^1.4.0",
- "optionator": "^0.8.3",
- "progress": "^2.0.0",
- "regexpp": "^2.0.1",
- "semver": "^6.1.2",
- "strip-ansi": "^5.2.0",
- "strip-json-comments": "^3.0.1",
- "table": "^5.2.3",
- "text-table": "^0.2.0",
- "v8-compile-cache": "^2.0.3"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
- "dev": true
- },
- "glob-parent": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
- "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "globals": {
- "version": "12.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
- "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
- "dev": true,
- "requires": {
- "type-fest": "^0.8.1"
- }
- },
- "import-fresh": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
- "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
- "dev": true,
- "requires": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "optionator": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
- "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
- "dev": true,
- "requires": {
- "deep-is": "~0.1.3",
- "fast-levenshtein": "~2.0.6",
- "levn": "~0.3.0",
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2",
- "word-wrap": "~1.2.3"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "strip-json-comments": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
- "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
- "dev": true
- },
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- },
- "v8-compile-cache": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
- "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
- "dev": true
- }
+ "license": "MIT",
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
+ "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
+ "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
+ "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
+ "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
}
},
- "eslint-config-prettier": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
- "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==",
+ "node_modules/@svgr/core": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
+ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
"dev": true,
- "requires": {
- "get-stdin": "^6.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^8.1.3",
+ "snake-case": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
}
},
- "eslint-config-react-app": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.0.tgz",
- "integrity": "sha512-WrHjoGpKr1kLLiWDD81tme9jMM0hk5cMxasLSdyno6DdPt+IfLOrDJBVo6jN7tn4y1nzhs43TmUaZWO6Sf0blw==",
+ "node_modules/@svgr/core/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"dev": true,
- "requires": {
- "confusing-browser-globals": "^1.0.9"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "eslint-config-standard": {
- "version": "14.1.1",
- "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz",
- "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==",
- "dev": true
+ "node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
+ "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.21.3",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
},
- "eslint-config-standard-jsx": {
+ "node_modules/@svgr/plugin-jsx": {
"version": "8.1.0",
- "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz",
- "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
+ "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "@svgr/hast-util-to-babel-ast": "8.0.0",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "*"
+ }
},
- "eslint-config-standard-react": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/eslint-config-standard-react/-/eslint-config-standard-react-9.2.0.tgz",
- "integrity": "sha512-u+KRP2uCtthZ/W4DlLWCC59GZNV/y9k9yicWWammgTs/Omh8ZUUPF3EnYm81MAcbkYQq2Wg0oxutAhi/FQ8mIw==",
+ "node_modules/@svgr/plugin-svgo": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz",
+ "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==",
"dev": true,
- "requires": {
- "eslint-config-standard-jsx": "^8.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "cosmiconfig": "^8.1.3",
+ "deepmerge": "^4.3.1",
+ "svgo": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "*"
}
},
- "eslint-import-resolver-node": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz",
- "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==",
+ "node_modules/@svgr/webpack": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz",
+ "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==",
"dev": true,
- "requires": {
- "debug": "^2.6.9",
- "resolve": "^1.13.1"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@babel/plugin-transform-react-constant-elements": "^7.21.3",
+ "@babel/preset-env": "^7.20.2",
+ "@babel/preset-react": "^7.18.6",
+ "@babel/preset-typescript": "^7.21.0",
+ "@svgr/core": "8.1.0",
+ "@svgr/plugin-jsx": "8.1.0",
+ "@svgr/plugin-svgo": "8.1.0"
+ },
+ "engines": {
+ "node": ">=14"
},
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- }
+ "defer-to-connect": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
}
},
- "eslint-loader": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz",
- "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==",
+ "node_modules/@tokenizer/inflate": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
+ "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==",
"dev": true,
- "requires": {
- "loader-fs-cache": "^1.0.0",
- "loader-utils": "^1.0.2",
- "object-assign": "^4.0.1",
- "object-hash": "^1.1.4",
- "rimraf": "^2.6.1"
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "token-types": "^6.1.1"
},
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/@tokenizer/inflate/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "eslint-module-utils": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
- "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "node_modules/@tokenizer/inflate/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
- "requires": {
- "debug": "^2.6.9",
- "pkg-dir": "^2.0.0"
- }
+ "license": "MIT"
},
- "eslint-plugin-chai-friendly": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz",
- "integrity": "sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ==",
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"dev": true
},
- "eslint-plugin-cypress": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.10.3.tgz",
- "integrity": "sha512-CvFeoCquShfO8gHNIKA1VpUTz78WtknMebLemBd1lRbcmJNjwpqCqpQYUG/XVja8GjdX/e2TJXYa+EUBxehtUg==",
+ "node_modules/@tsd/typescript": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.9.2.tgz",
+ "integrity": "sha512-mSMM0QtEPdMd+rdMDd17yCUYD4yI3pKHap89+jEZrZ3KIO5PhDofBjER0OtgHdvOXF74KMLO3fyD6k3Hz0v03A==",
"dev": true,
- "requires": {
- "globals": "^11.12.0"
- },
- "dependencies": {
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.17"
}
},
- "eslint-plugin-es": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz",
- "integrity": "sha512-6/Jb/J/ZvSebydwbBJO1R9E5ky7YeElfK56Veh7e4QGFHCXoIXGH9HhVz+ibJLM3XJ1XjP+T7rKBLUa/Y7eIng==",
- "dev": true,
- "requires": {
- "eslint-utils": "^2.0.0",
- "regexpp": "^3.0.0"
- },
- "dependencies": {
- "eslint-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
- "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "regexpp": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz",
- "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==",
- "dev": true
- }
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.1.tgz",
+ "integrity": "sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
- "eslint-plugin-flowtype": {
- "version": "3.13.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.13.0.tgz",
- "integrity": "sha512-bhewp36P+t7cEV0b6OdmoRWJCBYRiHFlqPZAG1oS3SF+Y0LQkeDvFSM4oxoxvczD1OdONCXMlJfQFiWLcV9urw==",
+ "node_modules/@types/acorn": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
+ "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==",
"dev": true,
- "requires": {
- "lodash": "^4.17.15"
+ "dependencies": {
+ "@types/estree": "*"
}
},
- "eslint-plugin-graphql": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-3.1.1.tgz",
- "integrity": "sha512-VNu2AipS8P1BAnE/tcJ2EmBWjFlCnG+1jKdUlFNDQjocWZlFiPpMu9xYNXePoEXK+q+jG51M/6PdhOjEgJZEaQ==",
- "dev": true,
- "requires": {
- "graphql-config": "^2.0.1",
- "lodash": "^4.11.1"
- }
- },
- "eslint-plugin-import": {
- "version": "2.20.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz",
- "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==",
- "dev": true,
- "requires": {
- "array-includes": "^3.0.3",
- "array.prototype.flat": "^1.2.1",
- "contains-path": "^0.1.0",
- "debug": "^2.6.9",
- "doctrine": "1.5.0",
- "eslint-import-resolver-node": "^0.3.2",
- "eslint-module-utils": "^2.4.1",
- "has": "^1.0.3",
- "minimatch": "^3.0.4",
- "object.values": "^1.1.0",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.12.0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
- "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "string.prototype.trimleft": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
- "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimstart": "^1.0.0"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
- "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimend": "^1.0.0"
- }
- }
- }
- },
- "eslint-plugin-jsdoc": {
- "version": "20.4.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-20.4.0.tgz",
- "integrity": "sha512-c/fnEpwWLFeQn+A7pb1qLOdyhovpqGCWCeUv1wtzFNL5G+xedl9wHUnXtp3b1sGHolVimi9DxKVTuhK/snXoOw==",
- "dev": true,
- "requires": {
- "comment-parser": "^0.7.2",
- "debug": "^4.1.1",
- "jsdoctypeparser": "^6.1.0",
- "lodash": "^4.17.15",
- "object.entries-ponyfill": "^1.0.1",
- "regextras": "^0.7.0",
- "semver": "^6.3.0",
- "spdx-expression-parse": "^3.0.0"
- },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
- "dev": true,
- "requires": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "spdx-license-ids": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
- "dev": true
- }
+ "@types/connect": "*",
+ "@types/node": "*"
}
},
- "eslint-plugin-jsx-a11y": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
- "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
+ "node_modules/@types/bonjour": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz",
+ "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.4.5",
- "aria-query": "^3.0.0",
- "array-includes": "^3.0.3",
- "ast-types-flow": "^0.0.7",
- "axobject-query": "^2.0.2",
- "damerau-levenshtein": "^1.0.4",
- "emoji-regex": "^7.0.2",
- "has": "^1.0.3",
- "jsx-ast-utils": "^2.2.1"
- },
+ "license": "MIT",
"dependencies": {
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- }
+ "@types/node": "*"
}
},
- "eslint-plugin-mocha": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz",
- "integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==",
- "dev": true,
- "requires": {
- "eslint-utils": "^2.0.0",
- "ramda": "^0.27.0"
- },
- "dependencies": {
- "eslint-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
- "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
- "dev": true
- },
- "ramda": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz",
- "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==",
- "dev": true
- }
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
}
},
- "eslint-plugin-no-extension-in-require": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-no-extension-in-require/-/eslint-plugin-no-extension-in-require-0.2.0.tgz",
- "integrity": "sha1-4ZMRC4sf+AMuK1YSPzFRwY+Tqr4=",
- "dev": true
+ "node_modules/@types/connect-history-api-fallback": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz",
+ "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/express-serve-static-core": "*",
+ "@types/node": "*"
+ }
},
- "eslint-plugin-node": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
- "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
- "requires": {
- "eslint-plugin-es": "^3.0.0",
- "eslint-utils": "^2.0.0",
- "ignore": "^5.1.1",
- "minimatch": "^3.0.4",
- "resolve": "^1.10.1",
- "semver": "^6.1.0"
- },
- "dependencies": {
- "eslint-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
- "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "ignore": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
- "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "dependencies": {
+ "@types/ms": "*"
}
},
- "eslint-plugin-promise": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz",
- "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==",
- "dev": true
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
},
- "eslint-plugin-react": {
- "version": "7.19.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz",
- "integrity": "sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==",
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dev": true,
- "requires": {
- "array-includes": "^3.1.1",
- "doctrine": "^2.1.0",
- "has": "^1.0.3",
- "jsx-ast-utils": "^2.2.3",
- "object.entries": "^1.1.1",
- "object.fromentries": "^2.0.2",
- "object.values": "^1.1.1",
- "prop-types": "^15.7.2",
- "resolve": "^1.15.1",
- "semver": "^6.3.0",
- "string.prototype.matchall": "^4.0.2",
- "xregexp": "^4.3.0"
- },
- "dependencies": {
- "array-includes": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
- "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
- "is-string": "^1.0.5"
- }
- },
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "object.entries": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz",
- "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "xregexp": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
- "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
- "dev": true,
- "requires": {
- "@babel/runtime-corejs3": "^7.8.3"
- }
- }
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
}
},
- "eslint-plugin-react-hooks": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz",
- "integrity": "sha512-Y2c4b55R+6ZzwtTppKwSmK/Kar8AdLiC2f9NADCuxbcTgPPg41Gyqa6b9GppgXSvCtkRw43ZE86CT5sejKC6/g==",
- "dev": true
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
},
- "eslint-plugin-sort-class-members": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-sort-class-members/-/eslint-plugin-sort-class-members-1.7.0.tgz",
- "integrity": "sha512-p9VeMf4lwJs4VJ9jDJRadXPklNGIdUYxeCLi0tjF3F83KNC6CfX+c3H2h0DQ2yLl3hgKf0j6s3wGpZUuRYToeA==",
- "dev": true
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*"
+ }
},
- "eslint-plugin-standard": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz",
- "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==",
- "dev": true
+ "node_modules/@types/express": {
+ "version": "4.17.25",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "^1"
+ }
},
- "eslint-scope": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
- "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.35",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz",
+ "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==",
"dev": true,
- "requires": {
- "esrecurse": "^4.1.0",
- "estraverse": "^4.1.1"
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
}
},
- "eslint-utils": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
- "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- },
"dependencies": {
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
- "dev": true
- }
+ "@types/unist": "*"
}
},
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "node_modules/@types/history": {
+ "version": "4.7.11",
+ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
+ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true
},
- "espree": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
- "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
+ "node_modules/@types/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
"dev": true,
- "requires": {
- "acorn": "^7.1.0",
- "acorn-jsx": "^5.1.0",
- "eslint-visitor-keys": "^1.1.0"
- },
+ "license": "MIT"
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/http-proxy": {
+ "version": "1.17.17",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz",
+ "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
- "dev": true
- }
+ "@types/node": "*"
}
},
- "esprima": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
- "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+ "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+ "dev": true
},
- "esquery": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
- "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
"dev": true,
- "requires": {
- "estraverse": "^4.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
}
},
- "esrecurse": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
- "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
"dev": true,
- "requires": {
- "estraverse": "^4.1.0",
- "object-assign": "^4.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
}
},
- "estraverse": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
- "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
- },
- "estree-walker": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
- "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
- "esutils": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
- "event-source-polyfill": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.12.tgz",
- "integrity": "sha512-WjOTn0LIbaN08z/8gNt3GYAomAdm6cZ2lr/QdvhTTEipr5KR6lds2ziUH+p/Iob4Lk6NClKhwPOmn1NjQEcJCg==",
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"dev": true
},
- "event-stream": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
- "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==",
"dev": true,
- "requires": {
- "duplexer": "~0.1.1",
- "from": "~0",
- "map-stream": "~0.1.0",
- "pause-stream": "0.0.11",
- "split": "0.3",
- "stream-combiner": "~0.0.4",
- "through": "~2.3.1"
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
}
},
- "event-target-shim": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
- "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "node_modules/@types/mdast": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz",
+ "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"dev": true
},
- "eventemitter2": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz",
- "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=",
+ "node_modules/@types/mdx": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.11.tgz",
+ "integrity": "sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==",
"dev": true
},
- "eventemitter3": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
- "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
+ "node_modules/@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true
},
- "events": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
- "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
+ "node_modules/@types/minimist": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
+ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
"dev": true
},
- "eventsource": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
- "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
- "dev": true,
- "requires": {
- "original": "^1.0.0"
- }
+ "node_modules/@types/ms": {
+ "version": "0.7.34",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
+ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
+ "dev": true
},
- "evp_bytestokey": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
- "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
- "dev": true,
- "requires": {
- "md5.js": "^1.3.4",
- "safe-buffer": "^5.1.1"
- }
+ "node_modules/@types/node": {
+ "version": "18.17.16",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.16.tgz",
+ "integrity": "sha512-e0zgs7qe1XH/X3KEPnldfkD07LH9O1B9T31U8qoO7lqGSjj3/IrBuvqMeJ1aYejXRK3KOphIUDw6pLIplEW17A==",
+ "dev": true
},
- "execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "dev": true,
- "requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
+ "node_modules/@types/normalize-package-data": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
+ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
+ "dev": true
},
- "executable": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
- "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
- "dev": true,
- "requires": {
- "pify": "^2.2.0"
- }
+ "node_modules/@types/prismjs": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
+ "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
+ "dev": true
},
- "exenv": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
- "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=",
+ "node_modules/@types/prop-types": {
+ "version": "15.7.11",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
+ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
"dev": true
},
- "exit-hook": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
- "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
- "expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "node_modules/@types/react": {
+ "version": "18.2.58",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.58.tgz",
+ "integrity": "sha512-TaGvMNhxvG2Q0K0aYxiKfNDS5m5ZsoIBBbtfUorxdH4NGSXIlYvZxLJI+9Dd3KjeB3780bciLyAb7ylO8pLhPw==",
"dev": true,
- "requires": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
"dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "dev": true
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- }
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
}
},
- "expand-tilde": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
- "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "node_modules/@types/react-router": {
+ "version": "5.1.20",
+ "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
+ "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
"dev": true,
- "requires": {
- "homedir-polyfill": "^1.0.1"
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*"
}
},
- "express": {
- "version": "4.17.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
- "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "node_modules/@types/react-router-config": {
+ "version": "5.0.11",
+ "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz",
+ "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==",
"dev": true,
- "requires": {
- "accepts": "~1.3.7",
- "array-flatten": "1.1.1",
- "body-parser": "1.19.0",
- "content-disposition": "0.5.3",
- "content-type": "~1.0.4",
- "cookie": "0.4.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "~1.1.2",
- "fresh": "0.5.2",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.5",
- "qs": "6.7.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.1.2",
- "send": "0.17.1",
- "serve-static": "1.14.1",
- "setprototypeof": "1.1.1",
- "statuses": "~1.5.0",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
"dependencies": {
- "accepts": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
- "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
- "dev": true,
- "requires": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.2"
- }
- },
- "cookie": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
- "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
- "dev": true
- },
- "mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
- "dev": true,
- "requires": {
- "mime-db": "1.43.0"
- }
- },
- "negotiator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
- "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
- "dev": true
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
- "dev": true
- },
- "qs": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
- "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
- "dev": true
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- }
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router": "^5.1.0"
}
},
- "express-graphql": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.9.0.tgz",
- "integrity": "sha512-wccd9Lb6oeJ8yHpUs/8LcnGjFUUQYmOG9A5BNLybRdCzGw0PeUrtBxsIR8bfiur6uSW4OvPkVDoYH06z6/N9+w==",
+ "node_modules/@types/react-router-dom": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
+ "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
"dev": true,
- "requires": {
- "accepts": "^1.3.7",
- "content-type": "^1.0.4",
- "http-errors": "^1.7.3",
- "raw-body": "^2.4.1"
- },
- "dependencies": {
- "accepts": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
- "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
- "dev": true,
- "requires": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.2"
- }
- },
- "http-errors": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
- "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
- "dev": true,
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.4",
- "setprototypeof": "1.1.1",
- "statuses": ">= 1.5.0 < 2",
- "toidentifier": "1.0.0"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
- "dev": true,
- "requires": {
- "mime-db": "1.43.0"
- }
- },
- "negotiator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
- "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
- "dev": true
- },
- "raw-body": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
- "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==",
- "dev": true,
- "requires": {
- "bytes": "3.1.0",
- "http-errors": "1.7.3",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- }
- }
+ "license": "MIT",
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router": "*"
}
},
- "extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ "node_modules/@types/retry": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
+ "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
+ "dev": true,
+ "license": "MIT"
},
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "node_modules/@types/sax": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
+ "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
"dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
}
},
- "external-editor": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
- "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+ "node_modules/@types/scheduler": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
+ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
+ "dev": true
+ },
+ "node_modules/@types/send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
"dev": true,
- "requires": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
"dependencies": {
- "tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.2"
- }
- }
+ "@types/mime": "^1",
+ "@types/node": "*"
}
},
- "extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "node_modules/@types/serve-index": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz",
+ "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==",
"dev": true,
- "requires": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- }
+ "@types/express": "*"
}
},
- "extract-zip": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
- "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
- "dev": true,
- "requires": {
- "concat-stream": "^1.6.2",
- "debug": "^2.6.9",
- "mkdirp": "^0.5.4",
- "yauzl": "^2.10.0"
- },
- "dependencies": {
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "mkdirp": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- }
+ "node_modules/@types/serve-static": {
+ "version": "1.15.8",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
+ "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "*"
}
},
- "extsprintf": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ "node_modules/@types/sinonjs__fake-timers": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
+ "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==",
+ "dev": true
},
- "fast-deep-equal": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+ "node_modules/@types/sizzle": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
"dev": true
},
- "fast-glob": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
- "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==",
+ "node_modules/@types/sockjs": {
+ "version": "0.3.36",
+ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
+ "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==",
"dev": true,
- "requires": {
- "@mrmlnc/readdir-enhanced": "^2.2.1",
- "@nodelib/fs.stat": "^1.1.2",
- "glob-parent": "^3.1.0",
- "is-glob": "^4.0.0",
- "merge2": "^1.2.3",
- "micromatch": "^3.1.10"
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
}
},
- "fast-json-patch": {
- "version": "3.0.0-1",
- "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz",
- "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==",
+ "node_modules/@types/tmp": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz",
+ "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
+ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==",
"dev": true
},
- "fast-json-stable-stringify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
- "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
+ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
+ "dev": true,
+ "license": "MIT"
},
- "fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
},
- "fast-xml-parser": {
- "version": "3.16.0",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.16.0.tgz",
- "integrity": "sha512-U+bpScacfgnfNfIKlWHDu4u6rtOaCyxhblOLJ8sZPkhsjgGqdZmVPBhdOyvdMGCDt8CsAv+cssOP3NzQptNt2w=="
+ "node_modules/@types/yargs": {
+ "version": "17.0.35",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
+ "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
},
- "fastparse": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
- "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
- "dev": true
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true,
+ "license": "MIT"
},
- "fastq": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz",
- "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==",
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
+ "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
"dev": true,
- "requires": {
- "reusify": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/type-utils": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.58.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "faye-websocket": {
- "version": "0.11.3",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
- "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
- "requires": {
- "websocket-driver": ">=0.5.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
}
},
- "fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz",
+ "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==",
"dev": true,
- "requires": {
- "pend": "~1.2.0"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/typescript-estree": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "fetch-ponyfill": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-6.1.0.tgz",
- "integrity": "sha512-bLc7JCjpJZZUXVxbgwUhd72Q19MAokrCXOg/Akq+wl0uFLFLklFdBkZo5OpQ3kLI0oGqrKdx6t5Zo3QwXBRmbQ==",
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
+ "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
"dev": true,
- "requires": {
- "node-fetch": "~2.6.0"
- },
+ "license": "MIT",
"dependencies": {
- "node-fetch": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
- "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
- "dev": true
- }
+ "@typescript-eslint/tsconfig-utils": "^8.65.0",
+ "@typescript-eslint/types": "^8.65.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "figgy-pudding": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
- "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
- "dev": true
- },
- "figures": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
- "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
+ "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
"dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5"
- },
+ "license": "MIT",
"dependencies": {
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "file-entry-cache": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
- "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
+ "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
"dev": true,
- "requires": {
- "flat-cache": "^2.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "file-loader": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
- "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
+ "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
"dev": true,
- "requires": {
- "loader-utils": "^1.0.2",
- "schema-utils": "^0.4.5"
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "file-type": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/file-type/-/file-type-11.0.0.tgz",
- "integrity": "sha512-ixd0mHkDO8KJ1S+ANTM+cZoZgL+TB0txLMm9KjTndfOjFYuRmrUcOtmSEm+e9s7wrynZOvvRD/8LwMQ6a24Irg==",
- "dev": true
- },
- "file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
+ "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
"dev": true,
- "optional": true
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.65.0",
+ "@typescript-eslint/tsconfig-utils": "8.65.0",
+ "@typescript-eslint/types": "8.65.0",
+ "@typescript-eslint/visitor-keys": "8.65.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
},
- "filesize": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
- "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
- "dev": true
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.65.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
+ "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.65.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
},
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "node_modules/@typescript-eslint/parser/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "finalhandler": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
- "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
"dev": true,
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "statuses": "~1.5.0",
- "unpipe": "~1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "find-cache-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
- "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^2.0.0",
- "pkg-dir": "^3.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "make-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
- "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
- "dev": true,
- "requires": {
- "pify": "^4.0.1",
- "semver": "^5.6.0"
- }
- },
- "p-limit": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
- "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "dev": true
- },
- "pkg-dir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
- "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
- "dev": true,
- "requires": {
- "find-up": "^3.0.0"
- }
- },
- "semver": {
- "version": "5.7.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
- "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
- "dev": true
+ "node_modules/@typescript-eslint/parser/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "find-root": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
- "dev": true
- },
- "find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
- "requires": {
- "locate-path": "^2.0.0"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "flat": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
- "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
- "requires": {
- "is-buffer": "~2.0.3"
- },
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "is-buffer": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
- "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
- "dev": true
- }
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "flat-cache": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
- "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "node_modules/@typescript-eslint/parser/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
+ "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
"dev": true,
- "requires": {
- "flatted": "^2.0.0",
- "rimraf": "2.6.3",
- "write": "1.0.3"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.58.0",
+ "@typescript-eslint/types": "^8.58.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
},
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "flatted": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
- "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
- "dev": true
- },
- "fleau": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/fleau/-/fleau-16.2.0.tgz",
- "integrity": "sha1-ruZ14mI37qfkNGH69MYadIe9MkM="
+ "node_modules/@typescript-eslint/project-service/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
},
- "flush-write-stream": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
- "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
+ "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
"dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "readable-stream": "^2.3.6"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "folktale": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.3.2.tgz",
- "integrity": "sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ==",
- "dev": true
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
+ "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
},
- "follow-redirects": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
- "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
+ "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
"dev": true,
- "requires": {
- "debug": "=3.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
- "dev": true
+ "node_modules/@typescript-eslint/type-utils/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
},
- "foreach": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
- "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
},
- "foreground-child": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
- "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
"dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^3.0.2"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
- "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "forever-agent": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
},
- "fork-ts-checker-webpack-plugin": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz",
- "integrity": "sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
- "requires": {
- "babel-code-frame": "^6.22.0",
- "chalk": "^2.4.1",
- "chokidar": "^2.0.4",
- "micromatch": "^3.1.10",
- "minimatch": "^3.0.4",
- "semver": "^5.6.0",
- "tapable": "^1.0.0",
- "worker-rpc": "^0.1.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "dev": true,
- "requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "fsevents": "^1.2.7",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "form-data": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
- "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "formidable": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
- "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
- "forwarded": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
- "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
- "dev": true
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
},
- "fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
+ "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
"dev": true,
- "requires": {
- "map-cache": "^0.2.2"
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "framesync": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/framesync/-/framesync-4.0.4.tgz",
- "integrity": "sha512-mdP0WvVHe0/qA62KG2LFUAOiWLng5GLpscRlwzBxu2VXOp6B8hNs5C5XlFigsMgrfDrr2YbqTsgdWZTc4RXRMQ==",
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
"dev": true,
- "requires": {
- "hey-listen": "^1.0.8",
- "tslib": "^1.10.0"
- },
+ "license": "MIT",
"dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
- "dev": true
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
},
- "from": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
- "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
- "from2": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
- "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "readable-stream": "^2.0.0"
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
}
},
- "fromentries": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz",
- "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==",
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"dev": true
},
- "fs-exists-cached": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz",
- "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=",
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"dev": true
},
- "fs-exists-sync": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
- "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"dev": true
},
- "fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
"dependencies": {
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
- "dev": true
- }
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@xtuc/long": "4.2.2"
}
},
- "fs-readfile-promise": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-3.0.1.tgz",
- "integrity": "sha512-LsSxMeaJdYH27XrW7Dmq0Gx63mioULCRel63B5VeELYLavi1wF5s0XfsIdKDFdCL9hsfQ2qBvXJszQtQJ9h17A==",
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.11"
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
}
},
- "fs-write-stream-atomic": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
- "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "iferr": "^0.1.5",
- "imurmurhash": "^0.1.4",
- "readable-stream": "1 || 2"
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
}
},
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+ "dev": true
},
- "fsevents": {
- "version": "1.2.11",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz",
- "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==",
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"dev": true,
- "optional": true,
- "requires": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1",
- "node-pre-gyp": "*"
- },
"dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "3.2.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.6.0"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minipass": {
- "version": "2.9.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.3.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.9.0"
- }
- },
- "mkdirp": {
- "version": "0.5.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^3.2.6",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.14.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4.4.2"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "npm-normalize-package-bin": "^1.0.1"
- }
- },
- "npm-normalize-package-bin": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.4.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "readable-stream": {
- "version": "2.3.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.7.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.13",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.8.6",
- "minizlib": "^1.2.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.3"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "yallist": {
- "version": "3.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- }
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
}
},
- "fsos": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/fsos/-/fsos-1.1.6.tgz",
- "integrity": "sha512-44MKwAuDfB14pojgokzqEhavMO0s1vv4H+WhsmHYB8fmoJI6YUephlD30Vak6paE6bbY3xd3b3Wa7vAgSglk8A==",
- "requires": {
- "mkdirp": "~0.5.1",
- "promise": "~7.0.4"
- },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+ "dev": true,
"dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "requires": {
- "minimist": "0.0.8"
- }
- }
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
}
},
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
+ }
},
- "function.prototype.name": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.2.tgz",
- "integrity": "sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg==",
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "functions-have-names": "^1.2.0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.0-next.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz",
- "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.4",
- "is-regex": "^1.0.4",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.0",
- "string.prototype.trimright": "^2.1.0"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "functions-have-names": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.0.tgz",
- "integrity": "sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ==",
- "dev": true
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
- "dev": true
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- }
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
}
},
- "functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@xmldom/xmldom": {
+ "version": "0.9.10",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz",
+ "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.6"
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"dev": true
},
- "gatsby": {
- "version": "2.19.41",
- "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.19.41.tgz",
- "integrity": "sha512-vFEY8wXDafHM21xoDiI6ppktpnCKJSAOsZYj1vjko/mJgTm3Y6E50xlt7GU3UtLN5b8OE3KDHzBnnj2OQ18C4g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.5.5",
- "@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/polyfill": "^7.7.0",
- "@babel/runtime": "^7.7.6",
- "@babel/traverse": "^7.7.4",
- "@hapi/joi": "^15.1.1",
- "@mikaelkristiansson/domready": "^1.0.10",
- "@pieh/friendly-errors-webpack-plugin": "1.7.0-chalk-2",
- "@pmmmwh/react-refresh-webpack-plugin": "^0.2.0",
- "@reach/router": "^1.3.1",
- "@typescript-eslint/eslint-plugin": "^2.11.0",
- "@typescript-eslint/parser": "^2.11.0",
- "address": "1.1.2",
- "autoprefixer": "^9.7.3",
- "axios": "^0.19.0",
- "babel-core": "7.0.0-bridge.0",
- "babel-eslint": "^10.0.3",
- "babel-loader": "^8.0.6",
- "babel-plugin-add-module-exports": "^0.3.3",
- "babel-plugin-dynamic-import-node": "^2.3.0",
- "babel-plugin-remove-graphql-queries": "^2.7.24",
- "babel-preset-gatsby": "^0.2.35",
- "better-opn": "1.0.0",
- "better-queue": "^3.8.10",
- "bluebird": "^3.7.2",
- "browserslist": "^4.9.1",
- "cache-manager": "^2.10.1",
- "cache-manager-fs-hash": "^0.0.7",
- "chalk": "^2.4.2",
- "chokidar": "3.3.0",
- "common-tags": "^1.8.0",
- "compression": "^1.7.4",
- "convert-hrtime": "^3.0.0",
- "copyfiles": "^2.1.1",
- "core-js": "^2.6.11",
- "cors": "^2.8.5",
- "css-loader": "^1.0.1",
- "date-fns": "^2.10.0",
- "debug": "^3.2.6",
- "del": "^5.1.0",
- "detect-port": "^1.3.0",
- "devcert": "^1.0.2",
- "dotenv": "^8.2.0",
- "eslint": "^6.7.2",
- "eslint-config-react-app": "^5.1.0",
- "eslint-loader": "^2.2.1",
- "eslint-plugin-flowtype": "^3.13.0",
- "eslint-plugin-graphql": "^3.1.0",
- "eslint-plugin-import": "^2.19.1",
- "eslint-plugin-jsx-a11y": "^6.2.3",
- "eslint-plugin-react": "^7.17.0",
- "eslint-plugin-react-hooks": "^1.7.0",
- "event-source-polyfill": "^1.0.11",
- "express": "^4.17.1",
- "express-graphql": "^0.9.0",
- "fast-levenshtein": "^2.0.6",
- "file-loader": "^1.1.11",
- "flat": "^4.1.0",
- "fs-exists-cached": "1.0.0",
- "fs-extra": "^8.1.0",
- "gatsby-cli": "^2.10.9",
- "gatsby-core-utils": "^1.0.33",
- "gatsby-graphiql-explorer": "^0.2.35",
- "gatsby-link": "^2.2.30",
- "gatsby-plugin-page-creator": "^2.1.45",
- "gatsby-react-router-scroll": "^2.1.23",
- "gatsby-telemetry": "^1.1.55",
- "glob": "^7.1.6",
- "got": "8.3.2",
- "graphql": "^14.5.8",
- "graphql-compose": "^6.3.7",
- "graphql-playground-middleware-express": "^1.7.12",
- "hasha": "^5.1.0",
- "invariant": "^2.2.4",
- "is-relative": "^1.0.0",
- "is-relative-url": "^3.0.0",
- "is-wsl": "^2.1.1",
- "jest-worker": "^24.9.0",
- "json-loader": "^0.5.7",
- "json-stringify-safe": "^5.0.1",
- "latest-version": "5.1.0",
- "lodash": "^4.17.15",
- "lokijs": "^1.5.8",
- "md5": "^2.2.1",
- "md5-file": "^3.2.3",
- "micromatch": "^3.1.10",
- "mime": "^2.4.4",
- "mini-css-extract-plugin": "^0.8.0",
- "mitt": "^1.2.0",
- "mkdirp": "^0.5.1",
- "moment": "^2.24.0",
- "name-all-modules-plugin": "^1.0.1",
- "normalize-path": "^2.1.1",
- "null-loader": "^0.1.1",
- "opentracing": "^0.14.4",
- "optimize-css-assets-webpack-plugin": "^5.0.3",
- "p-defer": "^3.0.0",
- "parseurl": "^1.3.3",
- "physical-cpu-count": "^2.0.0",
- "pnp-webpack-plugin": "^1.5.0",
- "postcss-flexbugs-fixes": "^4.2.0",
- "postcss-loader": "^3.0.0",
- "prompts": "^2.3.0",
- "prop-types": "^15.7.2",
- "raw-loader": "^0.5.1",
- "react-dev-utils": "^4.2.3",
- "react-error-overlay": "^3.0.0",
- "react-hot-loader": "^4.12.18",
- "react-refresh": "^0.7.0",
- "redux": "^4.0.4",
- "redux-thunk": "^2.3.0",
- "semver": "^5.7.1",
- "shallow-compare": "^1.2.2",
- "sift": "^5.1.0",
- "signal-exit": "^3.0.2",
- "slugify": "^1.3.6",
- "socket.io": "^2.3.0",
- "stack-trace": "^0.0.10",
- "string-similarity": "^1.2.2",
- "style-loader": "^0.23.1",
- "terser-webpack-plugin": "^1.4.2",
- "true-case-path": "^2.2.1",
- "type-of": "^2.0.1",
- "url-loader": "^1.1.2",
- "util.promisify": "^1.0.0",
- "uuid": "^3.3.3",
- "v8-compile-cache": "^1.1.2",
- "webpack": "~4.41.2",
- "webpack-dev-middleware": "^3.7.2",
- "webpack-dev-server": "^3.9.0",
- "webpack-hot-middleware": "^2.25.0",
- "webpack-merge": "^4.2.2",
- "webpack-stats-plugin": "^0.3.0",
- "xstate": "^4.7.2",
- "yaml-loader": "^0.5.0"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz",
- "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==",
- "dev": true
- },
- "@babel/runtime": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
- "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "@hapi/joi": {
- "version": "15.1.1",
- "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz",
- "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==",
- "dev": true,
- "requires": {
- "@hapi/address": "2.x.x",
- "@hapi/bourne": "1.x.x",
- "@hapi/hoek": "8.x.x",
- "@hapi/topo": "3.x.x"
- }
- },
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "babel-plugin-macros": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
- "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.7.2",
- "cosmiconfig": "^6.0.0",
- "resolve": "^1.12.0"
- }
- },
- "babel-plugin-remove-graphql-queries": {
- "version": "2.7.24",
- "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.7.24.tgz",
- "integrity": "sha512-uyYSTt2ikv4QhPC1HSt5WnP9sJFCOytrN119NpXQtGL9AiXNGghrELnY4BxhDuJtw/Wjrw20eED1+BJLzfMs7Q==",
- "dev": true
- },
- "babel-preset-gatsby": {
- "version": "0.2.35",
- "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-0.2.35.tgz",
- "integrity": "sha512-zylN9yeFB2WJJUt4mndkbHu1yhZVNKIgc0lUEgO1BZYeH2Rhj0zBMmP7zzR1dpxvryI96+etn2raIvnW+TTXeA==",
- "dev": true,
- "requires": {
- "@babel/plugin-proposal-class-properties": "^7.7.4",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4",
- "@babel/plugin-proposal-optional-chaining": "^7.7.5",
- "@babel/plugin-syntax-dynamic-import": "^7.7.4",
- "@babel/plugin-transform-runtime": "^7.7.6",
- "@babel/plugin-transform-spread": "^7.7.4",
- "@babel/preset-env": "^7.7.6",
- "@babel/preset-react": "^7.7.4",
- "@babel/runtime": "^7.7.6",
- "babel-plugin-dynamic-import-node": "^2.3.0",
- "babel-plugin-macros": "^2.8.0",
- "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
- "gatsby-core-utils": "^1.0.33"
- }
- },
- "boxen": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz",
- "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==",
- "dev": true,
- "requires": {
- "ansi-align": "^3.0.0",
- "camelcase": "^5.3.1",
- "chalk": "^2.4.2",
- "cli-boxes": "^2.2.0",
- "string-width": "^3.0.0",
- "term-size": "^1.2.0",
- "type-fest": "^0.3.0",
- "widest-line": "^2.0.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "configstore": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
- "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^3.0.0",
- "unique-string": "^2.0.0",
- "write-file-atomic": "^3.0.0",
- "xdg-basedir": "^4.0.0"
- }
- },
- "core-js": {
- "version": "2.6.11",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
- "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
- "dev": true
- },
- "cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "dev": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- }
- },
- "cross-spawn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
- "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "dev": true
- },
- "date-fns": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.10.0.tgz",
- "integrity": "sha512-EhfEKevYGWhWlZbNeplfhIU/+N+x0iCIx7VzKlXma2EdQyznVlZhCptXUY+BegNpPW2kjdx15Rvq503YcXXrcA==",
- "dev": true
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "eslint-plugin-react-hooks": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz",
- "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==",
- "dev": true
- },
- "execa": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz",
- "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "human-signals": "^1.1.1",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.0",
- "onetime": "^5.1.0",
- "p-finally": "^2.0.0",
- "signal-exit": "^3.0.2",
- "strip-final-newline": "^2.0.0"
- }
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "gatsby-cli": {
- "version": "2.10.9",
- "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.10.9.tgz",
- "integrity": "sha512-PfP3vwTH4CLaVw18SRJnfoDCKi4mzgZCHjUkmocBwAW8Qkvmp/N02Fd2aKTBkspUCPv5B761ZzuS32vUr74iUw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.5.5",
- "@babel/runtime": "^7.7.6",
- "@hapi/joi": "^15.1.1",
- "better-opn": "^1.0.0",
- "bluebird": "^3.7.2",
- "chalk": "^2.4.2",
- "clipboardy": "^2.1.0",
- "common-tags": "^1.8.0",
- "configstore": "^5.0.0",
- "convert-hrtime": "^3.0.0",
- "core-js": "^2.6.11",
- "envinfo": "^7.5.0",
- "execa": "^3.4.0",
- "fs-exists-cached": "^1.0.0",
- "fs-extra": "^8.1.0",
- "gatsby-core-utils": "^1.0.33",
- "gatsby-telemetry": "^1.1.55",
- "hosted-git-info": "^3.0.2",
- "ink": "^2.6.0",
- "ink-spinner": "^3.0.1",
- "is-valid-path": "^0.1.1",
- "lodash": "^4.17.15",
- "meant": "^1.0.1",
- "node-fetch": "^2.6.0",
- "object.entries": "^1.1.0",
- "opentracing": "^0.14.4",
- "pretty-error": "^2.1.1",
- "progress": "^2.0.3",
- "prompts": "^2.3.0",
- "react": "^16.8.0",
- "redux": "^4.0.4",
- "resolve-cwd": "^2.0.0",
- "semver": "^6.3.0",
- "signal-exit": "^3.0.2",
- "source-map": "0.7.3",
- "stack-trace": "^0.0.10",
- "strip-ansi": "^5.2.0",
- "update-notifier": "^3.0.1",
- "uuid": "3.3.3",
- "yargs": "^12.0.5",
- "yurnalist": "^1.1.1"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "gatsby-core-utils": {
- "version": "1.0.33",
- "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.0.33.tgz",
- "integrity": "sha512-eQkOQumfbMLdbKJN0E1dlnBjAKWAzexxuNdpL88yCIaqHGOMogGTmAmhG1Hs0sz9bMrNPxuIgEyDNQe3IDfJXw==",
- "dev": true,
- "requires": {
- "ci-info": "2.0.0",
- "configstore": "^5.0.0",
- "node-object-hash": "^2.0.0"
- }
- },
- "gatsby-page-utils": {
- "version": "0.0.44",
- "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-0.0.44.tgz",
- "integrity": "sha512-6N6+nptFpiFsjmOmPF7T/go9Hcd+5vhkhmArx5yRsJOv8kDs6LBOLh/d+mxRqT0Y3iQoXjUP5loy6li4syn4Hw==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.7.6",
- "bluebird": "^3.7.2",
- "chokidar": "3.3.0",
- "fs-exists-cached": "^1.0.0",
- "gatsby-core-utils": "^1.0.33",
- "glob": "^7.1.6",
- "lodash": "^4.17.15",
- "micromatch": "^3.1.10"
- }
- },
- "gatsby-plugin-page-creator": {
- "version": "2.1.45",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-2.1.45.tgz",
- "integrity": "sha512-mW5qfJ2C6U522wx63fZRTN2jCKG9FC/jkEmq4B0z5J/SOA9zPAlVvowjQAAW0+oGbEqwfIveSM+hIPwv1J7npA==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.7.6",
- "bluebird": "^3.7.2",
- "fs-exists-cached": "^1.0.0",
- "gatsby-page-utils": "^0.0.44",
- "glob": "^7.1.6",
- "lodash": "^4.17.15",
- "micromatch": "^3.1.10"
- }
- },
- "get-stream": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
- "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "got": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz",
- "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==",
- "dev": true,
- "requires": {
- "@sindresorhus/is": "^0.7.0",
- "cacheable-request": "^2.1.1",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^3.0.0",
- "into-stream": "^3.1.0",
- "is-retry-allowed": "^1.1.0",
- "isurl": "^1.0.0-alpha5",
- "lowercase-keys": "^1.0.0",
- "mimic-response": "^1.0.0",
- "p-cancelable": "^0.4.0",
- "p-timeout": "^2.0.1",
- "pify": "^3.0.0",
- "safe-buffer": "^5.1.1",
- "timed-out": "^4.0.1",
- "url-parse-lax": "^3.0.0",
- "url-to-options": "^1.0.1"
- },
- "dependencies": {
- "@sindresorhus/is": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
- "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==",
- "dev": true
- },
- "cacheable-request": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz",
- "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=",
- "dev": true,
- "requires": {
- "clone-response": "1.0.2",
- "get-stream": "3.0.0",
- "http-cache-semantics": "3.8.1",
- "keyv": "3.0.0",
- "lowercase-keys": "1.0.0",
- "normalize-url": "2.0.1",
- "responselike": "1.0.2"
- },
- "dependencies": {
- "lowercase-keys": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
- "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=",
- "dev": true
- }
- }
- },
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- },
- "p-cancelable": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz",
- "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==",
- "dev": true
- }
- }
- },
- "hosted-git-info": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz",
- "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==",
- "dev": true,
- "requires": {
- "lru-cache": "^5.1.1"
- }
- },
- "http-cache-semantics": {
- "version": "3.8.1",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
- "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
- "dev": true
- },
- "import-fresh": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
- "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
- "dev": true,
- "requires": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- }
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dev": true,
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "is-npm": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz",
- "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==",
- "dev": true
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
- },
- "is-wsl": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
- "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "keyv": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz",
- "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==",
- "dev": true,
- "requires": {
- "json-buffer": "3.0.0"
- }
- },
- "latest-version": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
- "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
- "dev": true,
- "requires": {
- "package-json": "^6.3.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "requires": {
- "yallist": "^3.0.2"
- }
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
- "dev": true
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "normalize-url": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz",
- "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==",
- "dev": true,
- "requires": {
- "prepend-http": "^2.0.0",
- "query-string": "^5.0.1",
- "sort-keys": "^2.0.0"
- }
- },
- "npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
- "onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "p-finally": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
- "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
- "dev": true
- },
- "p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "package-json": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
- "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
- "dev": true,
- "requires": {
- "got": "^9.6.0",
- "registry-auth-token": "^4.0.0",
- "registry-url": "^5.0.0",
- "semver": "^6.2.0"
- },
- "dependencies": {
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "dev": true,
- "requires": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "parse-json": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
- "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1",
- "lines-and-columns": "^1.1.6"
- }
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "query-string": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
- "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
- "dev": true,
- "requires": {
- "decode-uri-component": "^0.2.0",
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "registry-auth-token": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz",
- "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==",
- "dev": true,
- "requires": {
- "rc": "^1.2.8"
- }
- },
- "registry-url": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
- "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
- "dev": true,
- "requires": {
- "rc": "^1.2.8"
- }
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "dev": true
- },
- "strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "type-fest": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
- "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
- "dev": true
- },
- "unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dev": true,
- "requires": {
- "crypto-random-string": "^2.0.0"
- }
- },
- "update-notifier": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz",
- "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==",
- "dev": true,
- "requires": {
- "boxen": "^3.0.0",
- "chalk": "^2.0.1",
- "configstore": "^4.0.0",
- "has-yarn": "^2.1.0",
- "import-lazy": "^2.1.0",
- "is-ci": "^2.0.0",
- "is-installed-globally": "^0.1.0",
- "is-npm": "^3.0.0",
- "is-yarn-global": "^0.3.0",
- "latest-version": "^5.0.0",
- "semver-diff": "^2.0.0",
- "xdg-basedir": "^3.0.0"
- },
- "dependencies": {
- "configstore": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz",
- "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==",
- "dev": true,
- "requires": {
- "dot-prop": "^4.1.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^1.0.0",
- "unique-string": "^1.0.0",
- "write-file-atomic": "^2.0.0",
- "xdg-basedir": "^3.0.0"
- }
- },
- "crypto-random-string": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
- "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
- "dev": true
- },
- "dot-prop": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
- "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
- "dev": true,
- "requires": {
- "is-obj": "^1.0.0"
- }
- },
- "is-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
- "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
- "dev": true
- },
- "make-dir": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
- "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
- "dev": true,
- "requires": {
- "pify": "^3.0.0"
- }
- },
- "unique-string": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
- "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
- "dev": true,
- "requires": {
- "crypto-random-string": "^1.0.0"
- }
- },
- "write-file-atomic": {
- "version": "2.4.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
- "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.11",
- "imurmurhash": "^0.1.4",
- "signal-exit": "^3.0.2"
- }
- },
- "xdg-basedir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
- "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
- "dev": true
- }
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "xdg-basedir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
- "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
- "dev": true
- },
- "yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
- "yargs": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
- "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
- "dev": true,
- "requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^3.0.0",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
- "which-module": "^2.0.0",
- "y18n": "^3.2.1 || ^4.0.0",
- "yargs-parser": "^11.1.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "dev": true,
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
- }
- },
- "yargs-parser": {
- "version": "11.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
- "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
- }
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
},
- "gatsby-core-utils": {
+ "node_modules/abbrev": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.1.1.tgz",
- "integrity": "sha512-EboPcBx37YQVUKN9JH753S54nDxjRmOefbR0i08KTmaVgQ1lZnDXJr8JfrImmMqupZlOkPQX1mWlXfp+r1jGhA==",
- "dev": true,
- "requires": {
- "ci-info": "2.0.0",
- "configstore": "^5.0.1",
- "node-object-hash": "^2.0.0"
- },
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "configstore": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
- "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^3.0.0",
- "unique-string": "^2.0.0",
- "write-file-atomic": "^3.0.0",
- "xdg-basedir": "^4.0.0"
- }
- },
- "crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "dev": true
- },
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dev": true,
- "requires": {
- "crypto-random-string": "^2.0.0"
- }
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "xdg-basedir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
- "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
- "dev": true
- }
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "gatsby-graphiql-explorer": {
- "version": "0.2.35",
- "resolved": "https://registry.npmjs.org/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-0.2.35.tgz",
- "integrity": "sha512-y0ec6zLeaWwCus7xQnOS5dMX4Fu+//H6LLwPjODqnXAtnx2pT8MI5VYnYDqhMLHk32VEXYxhPJRvxg7VMkn18g==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.7.6"
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
},
- "dependencies": {
- "@babel/runtime": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
- "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "engines": {
+ "node": ">=0.4.0"
}
},
- "gatsby-link": {
- "version": "2.2.30",
- "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-2.2.30.tgz",
- "integrity": "sha512-fGUzQBHcARIYBVMno2qkyOykcxVdANLQpQ1R0kg8b6AEVjxReB+aUlXhq25nJkW4Z2E9Dhn32Xv4mk6znteNig==",
+ "node_modules/acorn-import-attributes": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^8"
+ }
+ },
+ "node_modules/acorn-import-phases": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
+ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.7.6",
- "@types/reach__router": "^1.2.6",
- "prop-types": "^15.7.2"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
},
- "dependencies": {
- "@babel/runtime": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
- "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "peerDependencies": {
+ "acorn": "^8.14.0"
}
},
- "gatsby-page-utils": {
- "version": "0.0.45",
- "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-0.0.45.tgz",
- "integrity": "sha512-StAEEFuHmtOwrNxD8g3ZmH3SLM+o3kU35AfVNEL8J74Woh5r6W2eg+7EPBae2TXIkrznWQUFuViBlPTuLBk06g==",
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7",
- "bluebird": "^3.7.2",
- "chokidar": "3.3.1",
- "fs-exists-cached": "^1.0.0",
- "gatsby-core-utils": "^1.0.34",
- "glob": "^7.1.6",
- "lodash": "^4.17.15",
- "micromatch": "^3.1.10"
- },
- "dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "anymatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
- "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "binary-extensions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
- "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
- "dev": true
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "chokidar": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
- "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
- "dev": true,
- "requires": {
- "anymatch": "~3.1.1",
- "braces": "~3.0.2",
- "fsevents": "~2.1.2",
- "glob-parent": "~5.1.0",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.3.0"
- }
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "fsevents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
- "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
- "dev": true,
- "optional": true
- },
- "glob-parent": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
- "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "readdirp": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
- "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
- "dev": true,
- "requires": {
- "picomatch": "^2.0.7"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- }
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
- "gatsby-plugin-catch-links": {
- "version": "2.1.28",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-catch-links/-/gatsby-plugin-catch-links-2.1.28.tgz",
- "integrity": "sha512-3cemo2r4Q1JcJEVqOfWhXcrNXgocoaDSEV1c2gogGemZcWIHLbq2dh0zRABu/QKZ4p6nb8uTgh8DHFugZ1zbdg==",
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7",
- "escape-string-regexp": "^1.0.5"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
}
},
- "gatsby-plugin-page-creator": {
- "version": "2.1.46",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-2.1.46.tgz",
- "integrity": "sha512-Yx8y2DYl/7wq+CR011/gVbfIXAC424hjZN/BNZVY22WQPZ3vsBScqa9B0awpJZ3OCzLKqWcK5iRHPxzQHUwE6g==",
+ "node_modules/address": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/address/-/address-2.0.3.tgz",
+ "integrity": "sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7",
- "bluebird": "^3.7.2",
- "fs-exists-cached": "^1.0.0",
- "gatsby-page-utils": "^0.0.45",
- "glob": "^7.1.6",
- "lodash": "^4.17.15",
- "micromatch": "^3.1.10"
- },
- "dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16.0.0"
}
},
- "gatsby-plugin-react-helmet": {
- "version": "3.1.24",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-3.1.24.tgz",
- "integrity": "sha512-kLR/RMDBVriJptsJufoL1UBVHgq2fZIMXen7nru2ugGn0m8xwpArFoOz6meYlpiDB3Z41eYR/+Nr8GizQnYcxg==",
+ "node_modules/after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7"
- },
"dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "gatsby-plugin-remove-trailing-slashes": {
- "version": "2.1.24",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-remove-trailing-slashes/-/gatsby-plugin-remove-trailing-slashes-2.1.24.tgz",
- "integrity": "sha512-4YZ+7JdQCxiaEPuFtGPMXYsg8LHJ+BdTWNex+dGA82AniaF5yn2ZAcMLbNelD71MVTjw2IYtyPJjgLZz6f5W2A==",
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7"
- },
"dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
}
},
- "gatsby-plugin-styled-components": {
- "version": "3.1.21",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-styled-components/-/gatsby-plugin-styled-components-3.1.21.tgz",
- "integrity": "sha512-uU4MtUAw3zazeVnKnOL0c0+mhtpNfEKee983jyUnAGZnXyXa9EYfZ7Lw+b++FqCce+e6EDlI7VemUR4voSD0DQ==",
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.8.7"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
- }
- },
- "gatsby-plugin-typescript": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/gatsby-plugin-typescript/-/gatsby-plugin-typescript-2.2.5.tgz",
- "integrity": "sha512-C17SW9tW08xUDl6K6niwY0FUi4INMVb5n29+6QwMGJw4Nxrztt3l99C6rKGR4qogsNjNEW0T/xRp2ehuHY/9rw==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.8.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-proposal-numeric-separator": "^7.8.3",
- "@babel/plugin-proposal-optional-chaining": "^7.8.3",
- "@babel/preset-typescript": "^7.8.3",
- "@babel/runtime": "^7.8.7",
- "babel-plugin-remove-graphql-queries": "^2.7.26"
- },
- "dependencies": {
- "@babel/helper-plugin-utils": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
- "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
- "dev": true
- },
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
- }
- },
- "@babel/plugin-proposal-optional-chaining": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz",
- "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
}
}
},
- "gatsby-react-router-scroll": {
- "version": "2.1.23",
- "resolved": "https://registry.npmjs.org/gatsby-react-router-scroll/-/gatsby-react-router-scroll-2.1.23.tgz",
- "integrity": "sha512-yUCWzRYUDgvr3xy5GAYd08gToBfE84SX3zvHWgPunVeL4OfwsYh6eei6GtYbLIjq77bvegd2SZkSujQ4Vgm/Gg==",
+ "node_modules/ajv-formats/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.7.6",
- "scroll-behavior": "^0.9.10",
- "warning": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
- "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "warning": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
- "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- }
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
}
},
- "gatsby-telemetry": {
- "version": "1.1.55",
- "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.55.tgz",
- "integrity": "sha512-6J0y+WaXLV9iMJnT8XZgK4GeFlN0pY17LHqfYhDHaTteAZTOsCgh28yJPXkKQEN+M2ahmKkq4buD1xSveT5r7A==",
+ "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
- "requires": {
- "@babel/code-frame": "^7.5.5",
- "@babel/runtime": "^7.7.6",
- "bluebird": "^3.7.2",
- "boxen": "^4.2.0",
- "configstore": "^5.0.0",
- "envinfo": "^7.5.0",
- "fs-extra": "^8.1.0",
- "gatsby-core-utils": "^1.0.33",
- "git-up": "4.0.1",
- "is-docker": "2.0.0",
- "lodash": "^4.17.15",
- "node-fetch": "2.6.0",
- "resolve-cwd": "^2.0.0",
- "source-map": "^0.7.3",
- "stack-trace": "^0.0.10",
- "stack-utils": "1.0.2",
- "uuid": "3.3.3"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
- "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.8.3"
- }
- },
- "@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.0",
- "esutils": "^2.0.2",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/runtime": {
- "version": "7.8.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
- "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "configstore": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
- "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^3.0.0",
- "unique-string": "^2.0.0",
- "write-file-atomic": "^3.0.0",
- "xdg-basedir": "^4.0.0"
- }
- },
- "crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "dev": true
- },
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "gatsby-core-utils": {
- "version": "1.0.33",
- "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.0.33.tgz",
- "integrity": "sha512-eQkOQumfbMLdbKJN0E1dlnBjAKWAzexxuNdpL88yCIaqHGOMogGTmAmhG1Hs0sz9bMrNPxuIgEyDNQe3IDfJXw==",
- "dev": true,
- "requires": {
- "ci-info": "2.0.0",
- "configstore": "^5.0.0",
- "node-object-hash": "^2.0.0"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "dev": true
- },
- "unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dev": true,
- "requires": {
- "crypto-random-string": "^2.0.0"
- }
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "xdg-basedir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
- "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
- "dev": true
- }
+ "license": "MIT"
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": "^6.9.1"
}
},
- "gensync": {
- "version": "1.0.0-beta.1",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
- "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
- "dev": true
+ "node_modules/algoliasearch": {
+ "version": "5.56.0",
+ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.56.0.tgz",
+ "integrity": "sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/abtesting": "1.22.0",
+ "@algolia/client-abtesting": "5.56.0",
+ "@algolia/client-analytics": "5.56.0",
+ "@algolia/client-common": "5.56.0",
+ "@algolia/client-insights": "5.56.0",
+ "@algolia/client-personalization": "5.56.0",
+ "@algolia/client-query-suggestions": "5.56.0",
+ "@algolia/client-search": "5.56.0",
+ "@algolia/ingestion": "1.56.0",
+ "@algolia/monitoring": "1.56.0",
+ "@algolia/recommend": "5.56.0",
+ "@algolia/requester-browser-xhr": "5.56.0",
+ "@algolia/requester-fetch": "5.56.0",
+ "@algolia/requester-node-http": "5.56.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
},
- "get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
- "dev": true
+ "node_modules/algoliasearch-helper": {
+ "version": "3.29.2",
+ "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.29.2.tgz",
+ "integrity": "sha512-SaV+rZM3drExb0punEYYjT+sNcH74YFwN8ocjya7IDOyQvKWeQpEaSMVG3+IGTVos+feuatj7ljQ4BXlXdUp3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/events": "^4.0.1"
+ },
+ "peerDependencies": {
+ "algoliasearch": ">= 3.1 < 6"
+ }
},
- "get-func-name": {
+ "node_modules/anafanafo": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
+ "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz",
+ "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==",
+ "dependencies": {
+ "char-width-table-consumer": "^1.0.0"
+ }
},
- "get-own-enumerable-property-symbols": {
+ "node_modules/ansi-align": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz",
- "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA==",
- "dev": true
- },
- "get-port": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
- "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
},
- "get-stdin": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
- "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
- "dev": true
+ "node_modules/ansi-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
},
- "get-stream": {
+ "node_modules/ansi-align/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
- "dev": true
- },
- "get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
},
- "getos": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/getos/-/getos-3.1.4.tgz",
- "integrity": "sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==",
+ "node_modules/ansi-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "async": "^3.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
"dependencies": {
- "async": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
- "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==",
- "dev": true
- }
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "getpass": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
- "requires": {
- "assert-plus": "^1.0.0"
+ "node_modules/ansi-escapes/node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "git-config-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz",
- "integrity": "sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=",
+ "node_modules/ansi-html-community": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+ "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
"dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "fs-exists-sync": "^0.1.0",
- "homedir-polyfill": "^1.0.0"
+ "engines": [
+ "node >= 0.8.0"
+ ],
+ "license": "Apache-2.0",
+ "bin": {
+ "ansi-html": "bin/ansi-html"
}
},
- "git-up": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.1.tgz",
- "integrity": "sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw==",
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
- "requires": {
- "is-ssh": "^1.3.0",
- "parse-url": "^5.0.0"
+ "engines": {
+ "node": ">=8"
}
},
- "gitlab": {
- "version": "10.2.1",
- "resolved": "https://registry.npmjs.org/gitlab/-/gitlab-10.2.1.tgz",
- "integrity": "sha512-z+DxRF1C9uayVbocs9aJkJz+kGy14TSm1noB/rAIEBbXOkOYbjKxyuqJzt+0zeFpXFdgA0yq6DVVbvM7HIfGwg==",
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
- "requires": {
- "form-data": "^2.5.0",
- "humps": "^2.0.1",
- "ky": "^0.12.0",
- "ky-universal": "^0.3.0",
- "li": "^1.3.0",
- "query-string": "^6.8.2",
- "universal-url": "^2.0.0"
- },
"dependencies": {
- "form-data": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
- }
- }
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "node_modules/ansis": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
+ "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
}
},
- "glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
- "requires": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- },
"dependencies": {
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.0"
- }
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anynum": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+ "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
+ ]
+ },
+ "node_modules/are-docs-informative": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
+ "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
}
},
- "glob-to-regexp": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
- "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true
},
- "global": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
- "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
"dev": true,
- "requires": {
- "min-document": "^2.19.0",
- "process": "^0.11.10"
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "global-dirs": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
- "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
"dev": true,
- "requires": {
- "ini": "^1.3.4"
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "global-modules": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
- "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true,
- "requires": {
- "global-prefix": "^3.0.0"
+ "engines": {
+ "node": ">=8"
}
},
- "global-prefix": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
- "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
"dev": true,
- "requires": {
- "ini": "^1.3.5",
- "kind-of": "^6.0.2",
- "which": "^1.3.1"
- },
+ "license": "MIT",
"dependencies": {
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
- "dev": true
- },
- "kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "dev": true
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "globals": {
- "version": "11.8.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz",
- "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==",
- "dev": true
- },
- "globby": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz",
- "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==",
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
"dev": true,
- "requires": {
- "@types/glob": "^7.1.1",
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.0.3",
- "glob": "^7.1.3",
- "ignore": "^5.1.1",
- "merge2": "^1.2.3",
- "slash": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@nodelib/fs.stat": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz",
- "integrity": "sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==",
- "dev": true
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "fast-glob": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.0.4.tgz",
- "integrity": "sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "^2.0.1",
- "@nodelib/fs.walk": "^1.2.1",
- "glob-parent": "^5.0.0",
- "is-glob": "^4.0.1",
- "merge2": "^1.2.3",
- "micromatch": "^4.0.2"
- }
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "glob-parent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz",
- "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "ignore": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
- "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
- "dev": true
- },
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "micromatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
- "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
- "dev": true,
- "requires": {
- "braces": "^3.0.1",
- "picomatch": "^2.0.5"
- }
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- }
- }
- },
- "got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "dev": true,
- "requires": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- },
- "dependencies": {
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- }
- }
- },
- "graceful-fs": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
- "dev": true
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "graphql": {
- "version": "14.6.0",
- "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.6.0.tgz",
- "integrity": "sha512-VKzfvHEKybTKjQVpTFrA5yUq2S9ihcZvfJAtsDBBCuV6wauPu1xl/f9ehgVf0FcEJJs4vz6ysb/ZMkGigQZseg==",
- "requires": {
- "iterall": "^1.2.2"
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "graphql-compose": {
- "version": "6.3.8",
- "resolved": "https://registry.npmjs.org/graphql-compose/-/graphql-compose-6.3.8.tgz",
- "integrity": "sha512-o0/jzQEMIpSjryLKwmD1vGrCubiPxD0LxlGTgWDSu38TBepu2GhugC9gYgTEbtiCZAHPtvkZ90SzzABOWZyQLA==",
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
"dev": true,
- "requires": {
- "graphql-type-json": "^0.2.4",
- "object-path": "^0.11.4"
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "graphql-config": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz",
- "integrity": "sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ==",
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
"dev": true,
- "requires": {
- "graphql-import": "^0.7.1",
- "graphql-request": "^1.5.0",
- "js-yaml": "^3.10.0",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.4"
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "graphql-import": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz",
- "integrity": "sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw==",
+ "node_modules/arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+ },
+ "node_modules/arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
"dev": true,
- "requires": {
- "lodash": "^4.17.4",
- "resolve-from": "^4.0.0"
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "graphql-playground-html": {
- "version": "1.6.12",
- "resolved": "https://registry.npmjs.org/graphql-playground-html/-/graphql-playground-html-1.6.12.tgz",
- "integrity": "sha512-yOYFwwSMBL0MwufeL8bkrNDgRE7eF/kTHiwrqn9FiR9KLcNIl1xw9l9a+6yIRZM56JReQOHpbQFXTZn1IuSKRg==",
- "dev": true
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "license": "MIT"
},
- "graphql-playground-middleware-express": {
- "version": "1.7.12",
- "resolved": "https://registry.npmjs.org/graphql-playground-middleware-express/-/graphql-playground-middleware-express-1.7.12.tgz",
- "integrity": "sha512-17szgonnVSxWVrgblLRHHLjWnMUONfkULIwSunaMvYx8k5oG3yL86cyGCbHuDFUFkyr2swLhdfYl4mDfDXuvOA==",
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
- "requires": {
- "graphql-playground-html": "1.6.12"
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
}
},
- "graphql-request": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz",
- "integrity": "sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg==",
+ "node_modules/asn1js": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz",
+ "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==",
"dev": true,
- "requires": {
- "cross-fetch": "2.2.2"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "pvtsutils": "^1.3.6",
+ "pvutils": "^1.1.3",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
}
},
- "graphql-tag": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz",
- "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA=="
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
},
- "graphql-type-json": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.2.4.tgz",
- "integrity": "sha512-/tq02ayMQjrG4oDFDRLLrPk0KvJXue0nVXoItBe7uAdbNXjQUu+HYCBdAmPLQoseVzUKKMzrhq2P/sfI76ON6w==",
- "dev": true
+ "node_modules/astring": {
+ "version": "1.8.6",
+ "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz",
+ "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==",
+ "dev": true,
+ "bin": {
+ "astring": "bin/astring"
+ }
},
- "growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
- "dev": true
+ "node_modules/async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
+ "dev": true,
+ "license": "MIT"
},
- "gud": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
- "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==",
- "dev": true
+ "node_modules/async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
- "gzip-size": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
- "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
+ "node_modules/async-retry": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz",
+ "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==",
"dev": true,
- "requires": {
- "duplexer": "^0.1.1",
- "pify": "^4.0.1"
- },
"dependencies": {
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "dev": true
- }
+ "retry": "0.12.0"
}
},
- "handle-thing": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
- "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==",
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
- "har-schema": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
- },
- "har-validator": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
- "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
- "requires": {
- "ajv": "^6.5.5",
- "har-schema": "^2.0.0"
- }
- },
- "has": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
- "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
- "requires": {
- "function-bind": "^1.0.2"
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
}
},
- "has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "node_modules/autoprefixer": {
+ "version": "10.5.4",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz",
+ "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==",
"dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.6",
+ "caniuse-lite": "^1.0.30001806",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
}
},
- "has-binary2": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
- "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
- "requires": {
- "isarray": "2.0.1"
- },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
"dependencies": {
- "isarray": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
- }
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "has-cors": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
- "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
- },
- "has-flag": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
- "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
- "dev": true
- },
- "has-only": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/has-only/-/has-only-1.1.1.tgz",
- "integrity": "sha512-3GuFy9rDw0xvovCHb4SOKiRImbZ+a8boFBUyGNRPVd2mRyQOzYdau5G9nodUXC1ZKYN59hrHFkW1lgBQscYfTg==",
- "dev": true
- },
- "has-symbol-support-x": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
- "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==",
- "dev": true
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
},
- "has-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
- "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "node_modules/aws4": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
+ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==",
"dev": true
},
- "has-to-string-tag-x": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
- "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==",
+ "node_modules/axios": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
+ "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
"dev": true,
- "requires": {
- "has-symbol-support-x": "^1.4.1"
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
}
},
- "has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "node_modules/axios/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
- "requires": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
}
},
- "has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "node_modules/axios/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "has-yarn": {
+ "node_modules/axios/node_modules/proxy-from-env": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
- "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
- "dev": true
- },
- "hash-base": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
}
},
- "hash.js": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
- "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "node_modules/babel-loader": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
+ "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "minimalistic-assert": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "find-cache-dir": "^4.0.0",
+ "schema-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0",
+ "webpack": ">=5"
}
},
- "hasha": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz",
- "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==",
+ "node_modules/babel-plugin-dynamic-import-node": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
"dev": true,
- "requires": {
- "is-stream": "^2.0.0",
- "type-fest": "^0.8.0"
- },
+ "license": "MIT",
"dependencies": {
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
- },
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- }
+ "object.assign": "^4.1.0"
}
},
- "hasurl": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hasurl/-/hasurl-1.0.0.tgz",
- "integrity": "sha512-43ypUd3DbwyCT01UYpA99AEZxZ4aKtRxWGBHEIbjcOsUghd9YUON0C+JF6isNjaiwC/UF5neaUudy6JS9jZPZQ==",
- "dev": true
- },
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true
- },
- "hex-color-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
- "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==",
- "dev": true
- },
- "hey-listen": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz",
- "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==",
- "dev": true
- },
- "hmac-drbg": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
- "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz",
+ "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==",
"dev": true,
- "requires": {
- "hash.js": "^1.0.3",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.7",
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "hoist-non-react-statics": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
- "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==",
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
- "requires": {
- "react-is": "^16.7.0"
+ "bin": {
+ "semver": "bin/semver.js"
}
},
- "homedir-polyfill": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
- "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
+ "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
"dev": true,
- "requires": {
- "parse-passwd": "^1.0.0"
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "core-js-compat": "^3.38.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "hosted-git-info": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
- "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
- "dev": true
- },
- "hpack.js": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
- "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
- "requires": {
- "inherits": "^2.0.1",
- "obuf": "^1.0.0",
- "readable-stream": "^2.0.1",
- "wbuf": "^1.1.0"
- },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz",
+ "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
- },
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
+ "@babel/helper-define-polyfill-provider": "^0.6.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "hsl-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
- "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
- "dev": true
+ "node_modules/backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
},
- "hsla-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
- "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
- "dev": true
+ "node_modules/badge-maker": {
+ "resolved": "badge-maker",
+ "link": true
},
- "html": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
- "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=",
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
"dev": true,
- "requires": {
- "concat-stream": "^1.4.7"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "html-comment-regex": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
- "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
- "html-element-map": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.2.0.tgz",
- "integrity": "sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw==",
- "dev": true,
- "requires": {
- "array-filter": "^1.0.0"
- },
- "dependencies": {
- "array-filter": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
- "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=",
- "dev": true
- }
+ "node_modules/base64-arraybuffer": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+ "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==",
+ "engines": {
+ "node": ">= 0.6.0"
}
},
- "html-entities": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
- "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
- "dev": true
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
},
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
},
- "htmlparser2": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
- "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.0.tgz",
+ "integrity": "sha512-oCu2wfipvX3AePSgmOuKkIywOu+8n9psz7hXYmk56ghpu3+7KzNIBopaOs4c9BrtdnTtW30unG9GTfHo7EwERQ==",
"dev": true,
- "requires": {
- "domelementtype": "^1.3.1",
- "domhandler": "^2.3.0",
- "domutils": "^1.5.1",
- "entities": "^1.1.1",
- "inherits": "^2.0.1",
- "readable-stream": "^3.1.1"
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
},
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
"dependencies": {
- "domelementtype": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
- "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
- "dev": true
- },
- "readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
- "dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
+ "tweetnacl": "^0.14.3"
}
},
- "http-cache-semantics": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz",
- "integrity": "sha512-laeSTWIkuFa6lUgZAt+ic9RwOSEwbi9VDQNcCvMFO4sZiDc2Ha8DaZVCJnfpLLQCcS8rvCnIWYmz0POLxt7Dew==",
- "dev": true
+ "node_modules/before-after-hook": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
+ "dev": true,
+ "license": "Apache-2.0"
},
- "http-deceiver": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
- "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc="
+ "node_modules/big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
},
- "http-errors": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
- "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.1",
- "statuses": ">= 1.5.0 < 2",
- "toidentifier": "1.0.0"
+ "engines": {
+ "node": ">=8"
}
},
- "http-parser-js": {
- "version": "0.4.10",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
- "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
+ "node_modules/binary-search": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+ },
+ "node_modules/bintrees": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
+ "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="
+ },
+ "node_modules/blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+ },
+ "node_modules/blob-util": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz",
+ "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==",
"dev": true
},
- "http-proxy": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"dev": true,
- "requires": {
- "eventemitter3": "^4.0.0",
- "follow-redirects": "^1.0.0",
- "requires-port": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
}
},
- "http-proxy-agent": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
- "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+ "node_modules/body-parser/node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
- "requires": {
- "agent-base": "4",
- "debug": "3.1.0"
- },
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
+ "ms": "2.0.0"
}
},
- "http-proxy-middleware": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
- "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+ "node_modules/body-parser/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true,
- "requires": {
- "http-proxy": "^1.17.0",
- "is-glob": "^4.0.0",
- "lodash": "^4.17.11",
- "micromatch": "^3.1.10"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "http-signature": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
- "requires": {
- "assert-plus": "^1.0.0",
- "jsprim": "^1.2.2",
- "sshpk": "^1.7.0"
+ "node_modules/body-parser/node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "https-browserify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
- "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
- "dev": true
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
},
- "https-proxy-agent": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
- "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
- "requires": {
- "agent-base": "^4.3.0",
- "debug": "^3.1.0"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
},
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/bonjour-service": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz",
+ "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "fast-deep-equal": "^3.1.3",
+ "multicast-dns": "^7.2.5"
}
},
- "human-signals": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
- "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"dev": true
},
- "humanize-string": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/humanize-string/-/humanize-string-2.1.0.tgz",
- "integrity": "sha512-sQ+hqmxyXW8Cj7iqxcQxD7oSy3+AXnIZXdUF9lQMkzaG8dtbKAB8U7lCtViMnwQ+MpdCKsO2Kiij3G6UUXq/Xg==",
+ "node_modules/boxen": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz",
+ "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==",
"dev": true,
- "requires": {
- "decamelize": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.1.2",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^5.0.1",
+ "type-fest": "^2.5.0",
+ "widest-line": "^4.0.1",
+ "wrap-ansi": "^8.0.1"
},
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "decamelize": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz",
- "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==",
- "dev": true,
- "requires": {
- "xregexp": "4.0.0"
- }
- },
- "xregexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
- "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==",
- "dev": true
- }
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "humps": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
- "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=",
- "dev": true
+ "node_modules/boxen/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "husky": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/husky/-/husky-3.1.0.tgz",
- "integrity": "sha512-FJkPoHHB+6s4a+jwPqBudBDvYZsoQW5/HBuMSehC8qDiCe50kpcxeqFoDSlow+9I6wg47YxBoT3WxaURlrDIIQ==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.2",
- "ci-info": "^2.0.0",
- "cosmiconfig": "^5.2.1",
- "execa": "^1.0.0",
- "get-stdin": "^7.0.0",
- "opencollective-postinstall": "^2.0.2",
- "pkg-dir": "^4.2.0",
- "please-upgrade-node": "^3.2.0",
- "read-pkg": "^5.2.0",
- "run-node": "^1.0.0",
- "slash": "^3.0.0"
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
},
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "dev": true,
- "requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "get-stdin": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz",
- "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==",
- "dev": true
- },
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "dev": true,
- "requires": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "p-limit": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
- "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "parse-json": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
- "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1",
- "lines-and-columns": "^1.1.6"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "requires": {
- "find-up": "^4.0.0"
- }
- },
- "read-pkg": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
- "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
- "dev": true,
- "requires": {
- "@types/normalize-package-data": "^2.4.0",
- "normalize-package-data": "^2.5.0",
- "parse-json": "^5.0.0",
- "type-fest": "^0.6.0"
- }
- },
- "resolve": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.2.tgz",
- "integrity": "sha512-cAVTI2VLHWYsGOirfeYVVQ7ZDejtQ9fp4YhYckWDEkFfqbVjaT11iM8k6xSAfGFMM+gDpZjMnFssPu8we+mqFw==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "hyperlinker": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz",
- "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==",
- "dev": true
+ "node_modules/boxen/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
},
- "icedfrisby": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/icedfrisby/-/icedfrisby-3.0.0.tgz",
- "integrity": "sha512-sVqdmppWWyMUHM9daI68+AteZkGbL+Z0yfDrvo5aDvvM8MYbWYPyS0rOkAFMTzZLQyCb/196qlzhjDOLlW3iTQ==",
+ "node_modules/boxen/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "requires": {
- "@hapi/joi": ">=16.0.0",
- "chai": "^4.0.1",
- "chai-subset": "^1.3.0",
- "chai-things": "^0.2.0",
- "chalk": "^2.0.1",
- "check-types": "^10.0.0",
- "lodash": "^4.16.6",
- "qs": "^6.3.0",
- "request": "^2.76.0",
- "verror": "^1.10.0",
- "wait-promise": "^0.4.1"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "icedfrisby-nock": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/icedfrisby-nock/-/icedfrisby-nock-2.0.0.tgz",
- "integrity": "sha512-Yl0XmxqXgvjcqLpA8MhWKVQQHeuPB2Nrl1NLG0DoTp8FI0qIWOCal8xEZ7zBY5RSgCL83TX0vPyBOX9dR1G4zQ==",
- "dev": true
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
},
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "icss-replace-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
- "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
- "icss-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz",
- "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
+ "node_modules/browserslist": {
+ "version": "4.28.7",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz",
+ "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==",
"dev": true,
- "requires": {
- "postcss": "^6.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
},
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
},
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.44",
+ "caniuse-lite": "^1.0.30001806",
+ "electron-to-chromium": "^1.5.393",
+ "node-releases": "^2.0.51",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
},
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
}
},
- "ieee754": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
- "dev": true
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "dev": true,
+ "license": "BSD-3-Clause"
},
- "iferr": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
- "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
- "ignore": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
- "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
- "dev": true
+ "node_modules/bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "dependencies": {
+ "run-applescript": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "ignore-by-default": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
- "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
- "dev": true
+ "node_modules/byte-counter": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz",
+ "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "immer": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
- "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==",
- "dev": true
+ "node_modules/byte-size": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-9.0.1.tgz",
+ "integrity": "sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.17"
+ },
+ "peerDependencies": {
+ "@75lb/nature": "latest"
+ },
+ "peerDependenciesMeta": {
+ "@75lb/nature": {
+ "optional": true
+ }
+ }
},
- "import-cwd": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
- "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+ "node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
"dev": true,
- "requires": {
- "import-from": "^2.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "import-fresh": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
- "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "node_modules/bytestreamjs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz",
+ "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==",
"dev": true,
- "requires": {
- "caller-path": "^2.0.0",
- "resolve-from": "^3.0.0"
- },
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/c8": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/c8/-/c8-12.0.0.tgz",
+ "integrity": "sha512-4zpJvrd1nKWutnnKC2pXkFmb6iM1l+ffN//o1CzlTNwW7GSOs9a1xrLqkC48nU8oEkjmPZLPiwMsIaOvoF4Pqg==",
+ "dev": true,
+ "license": "ISC",
"dependencies": {
- "resolve-from": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
- "dev": true
+ "@bcoe/v8-coverage": "^1.0.1",
+ "@istanbuljs/schema": "^0.1.3",
+ "find-up": "^5.0.0",
+ "foreground-child": "^3.1.1",
+ "istanbul-lib-coverage": "^3.2.0",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-reports": "^3.1.6",
+ "test-exclude": "^8.0.0",
+ "v8-to-istanbul": "^9.0.0",
+ "yargs": "^18.0.0",
+ "yargs-parser": "^21.1.1"
+ },
+ "bin": {
+ "c8": "bin/c8.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ },
+ "peerDependencies": {
+ "monocart-coverage-reports": "^2"
+ },
+ "peerDependenciesMeta": {
+ "monocart-coverage-reports": {
+ "optional": true
}
}
},
- "import-from": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
- "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+ "node_modules/c8/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
- "requires": {
- "resolve-from": "^3.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
},
- "dependencies": {
- "resolve-from": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "import-lazy": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
- "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
- "dev": true
- },
- "import-local": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
- "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
- "dev": true,
- "requires": {
- "pkg-dir": "^3.0.0",
- "resolve-cwd": "^2.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "pkg-dir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
- "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
- "dev": true,
- "requires": {
- "find-up": "^3.0.0"
- }
- }
+ "node_modules/c8/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
- "dev": true
- },
- "indent-string": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
- "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
- "dev": true
- },
- "indexes-of": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
- "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
- "dev": true
- },
- "indexof": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+ "node_modules/c8/node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
},
- "infer-owner": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
- "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
- "dev": true
+ "node_modules/c8/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
},
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
+ "node_modules/c8/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- },
- "ini": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
- "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=",
- "dev": true
+ "node_modules/c8/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
},
- "ink": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/ink/-/ink-2.7.1.tgz",
- "integrity": "sha512-s7lJuQDJEdjqtaIWhp3KYHl6WV3J04U9zoQ6wVc+Xoa06XM27SXUY57qC5DO46xkF0CfgXMKkKNcgvSu/SAEpA==",
+ "node_modules/c8/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
"dev": true,
- "optional": true,
- "requires": {
- "ansi-escapes": "^4.2.1",
- "arrify": "^2.0.1",
- "auto-bind": "^4.0.0",
- "chalk": "^3.0.0",
- "cli-cursor": "^3.1.0",
- "cli-truncate": "^2.1.0",
- "is-ci": "^2.0.0",
- "lodash.throttle": "^4.1.1",
- "log-update": "^3.0.0",
- "prop-types": "^15.6.2",
- "react-reconciler": "^0.24.0",
- "scheduler": "^0.18.0",
- "signal-exit": "^3.0.2",
- "slice-ansi": "^3.0.0",
- "string-length": "^3.1.0",
- "widest-line": "^3.1.0",
- "wrap-ansi": "^6.2.0",
- "yoga-layout-prebuilt": "^1.9.3"
- },
- "dependencies": {
- "ansi-escapes": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
- "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
- "dev": true,
- "optional": true,
- "requires": {
- "type-fest": "^0.11.0"
- }
- },
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true,
- "optional": true
- },
- "ansi-styles": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
- "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
- "dev": true,
- "optional": true,
- "requires": {
- "@types/color-name": "^1.1.1",
- "color-convert": "^2.0.1"
- }
- },
- "arrify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
- "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
- "dev": true,
- "optional": true
- },
- "astral-regex": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
- "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
- "dev": true,
- "optional": true
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true,
- "optional": true
- },
- "cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
- "dev": true,
- "optional": true,
- "requires": {
- "restore-cursor": "^3.1.0"
- }
- },
- "cli-truncate": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
- "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
- "dev": true,
- "optional": true,
- "requires": {
- "slice-ansi": "^3.0.0",
- "string-width": "^4.2.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "optional": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "optional": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "optional": true
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dev": true,
- "optional": true,
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "optional": true
- },
- "log-update": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz",
- "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-escapes": "^3.2.0",
- "cli-cursor": "^2.1.0",
- "wrap-ansi": "^5.0.0"
- },
- "dependencies": {
- "ansi-escapes": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
- "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true,
- "optional": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "optional": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "cli-cursor": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
- "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
- "dev": true,
- "optional": true,
- "requires": {
- "restore-cursor": "^2.0.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "optional": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true,
- "optional": true
- },
- "emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true,
- "optional": true
- },
- "mimic-fn": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
- "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
- "dev": true,
- "optional": true
- },
- "onetime": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
- "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
- "dev": true,
- "optional": true,
- "requires": {
- "mimic-fn": "^1.0.0"
- }
- },
- "restore-cursor": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
- "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
- "dev": true,
- "optional": true,
- "requires": {
- "onetime": "^2.0.0",
- "signal-exit": "^3.0.2"
- }
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "optional": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "wrap-ansi": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
- "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "string-width": "^3.0.0",
- "strip-ansi": "^5.0.0"
- }
- }
- }
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
- "optional": true
- },
- "onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
- "dev": true,
- "optional": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
- "dev": true,
- "optional": true,
- "requires": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- }
- },
- "slice-ansi": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
- "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "astral-regex": "^2.0.0",
- "is-fullwidth-code-point": "^3.0.0"
- }
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "optional": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
- "type-fest": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
- "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
- "dev": true,
- "optional": true
- },
- "widest-line": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
- "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^4.0.0"
- }
- }
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "ink-spinner": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-3.0.1.tgz",
- "integrity": "sha512-AVR4Z/NXDQ7dT5ltWcCzFS9Dd4T8eaO//E2UO8VYNiJcZpPCSJ11o5A0UVPcMlZxGbGD6ikUFDR3ZgPUQk5haQ==",
+ "node_modules/c8/node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
"dev": true,
- "optional": true,
- "requires": {
- "cli-spinners": "^1.0.0",
- "prop-types": "^15.5.10"
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
- "inquirer": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz",
- "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==",
+ "node_modules/c8/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "requires": {
- "ansi-escapes": "^4.2.1",
- "chalk": "^2.4.2",
- "cli-cursor": "^3.1.0",
- "cli-width": "^2.0.0",
- "external-editor": "^3.0.3",
- "figures": "^3.0.0",
- "lodash": "^4.17.15",
- "mute-stream": "0.0.8",
- "run-async": "^2.2.0",
- "rxjs": "^6.5.3",
- "string-width": "^4.1.0",
- "strip-ansi": "^5.1.0",
- "through": "^2.3.6"
- },
- "dependencies": {
- "ansi-escapes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
- "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
- "dev": true,
- "requires": {
- "type-fest": "^0.8.1"
- }
- },
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
- "dev": true,
- "requires": {
- "restore-cursor": "^3.1.0"
- }
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "figures": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
- "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5"
- }
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "mute-stream": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
- "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
- "dev": true
- },
- "onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
- "dev": true,
- "requires": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- }
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- }
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- }
- }
- },
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true
- }
+ "engines": {
+ "node": ">=12"
}
},
- "internal-ip": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
- "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
+ "node_modules/c8/node_modules/yargs/node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
"dev": true,
- "requires": {
- "default-gateway": "^4.2.0",
- "ipaddr.js": "^1.9.0"
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
- "internal-slot": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz",
- "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==",
- "dev": true,
- "requires": {
- "es-abstract": "^1.17.0-next.1",
- "has": "^1.0.3",
- "side-channel": "^1.0.2"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
+ "node_modules/cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "engines": {
+ "node": ">=14.16"
}
},
- "into-stream": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
- "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
+ "node_modules/cacheable-request": {
+ "version": "10.2.14",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
"dev": true,
- "requires": {
- "from2": "^2.1.1",
- "p-is-promise": "^1.1.0"
- },
+ "license": "MIT",
"dependencies": {
- "p-is-promise": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
- "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
- "dev": true
- }
+ "@types/http-cache-semantics": "^4.0.2",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.3",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
}
},
- "invariant": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
- "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+ "node_modules/cacheable-request/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "invert-kv": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
- "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
- "dev": true
+ "node_modules/cachedir": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
},
- "ioredis": {
- "version": "4.16.3",
- "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.16.3.tgz",
- "integrity": "sha512-Ejvcs2yW19Vq8AipvbtfcX3Ig8XG9EAyFOvGbhI/Q1QoVOK9ZdgY092kdOyOWIYBnPHjfjMJhU9qhsnp0i0K1w==",
- "requires": {
- "cluster-key-slot": "^1.1.0",
- "debug": "^4.1.1",
- "denque": "^1.1.0",
- "lodash.defaults": "^4.2.0",
- "lodash.flatten": "^4.4.0",
- "redis-commands": "1.5.0",
- "redis-errors": "^1.2.0",
- "redis-parser": "^3.0.0",
- "standard-as-callback": "^2.0.1"
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "ip": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
- "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
- "dev": true
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "ip-regex": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
- "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
- "dev": true
+ "node_modules/call-me-maybe": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
+ "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
+ "dev": true,
+ "license": "MIT"
},
- "ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "node_modules/caller": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/caller/-/caller-1.1.0.tgz",
+ "integrity": "sha512-n+21IZC3j06YpCWaxmUy5AnVqhmCIM2bQtqQyy00HJlmStRt6kwDX5F9Z97pqwAB+G/tgSz6q/kUBbNyQzIubw==",
"dev": true
},
- "is-absolute-url": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
- "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
- "dev": true
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
},
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "node_modules/camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
"dev": true,
- "requires": {
- "kind-of": "^6.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true
- }
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
}
},
- "is-arguments": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
- "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
- "dev": true
+ "node_modules/camelcase": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-9.0.0.tgz",
+ "integrity": "sha512-TO9xmyXTZ9HUHI8M1OnvExxYB0eYVS/1e5s7IDMTAoIcwUd+aNcFODs6Xk83mobk0velyHFQgA1yIrvYc6wclw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
- "dev": true
+ "node_modules/camelo": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelo/-/camelo-1.2.1.tgz",
+ "integrity": "sha512-5O6lYuw6JR8viGfxFQhJTaO7cPTsnEaJUK2Gj0JeZ0O+iO78kxfeF47ocx6Ml0K8KWbE/JgBbgi3zF6lcUkvGw==",
+ "dependencies": {
+ "regex-escape": "^3.4.10",
+ "uc-first-array": "^1.1.10"
+ }
},
- "is-binary-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
- "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "node_modules/caniuse-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
"dev": true,
- "requires": {
- "binary-extensions": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
}
},
- "is-boolean-object": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz",
- "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==",
- "dev": true
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001806",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
+ "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
},
- "is-buffer": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
- "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
- "is-builtin-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
- "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "node_modules/catharsis": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
"dev": true,
- "requires": {
- "builtin-modules": "^1.0.0"
+ "dependencies": {
+ "lodash": "^4.17.15"
+ },
+ "engines": {
+ "node": ">= 10"
}
},
- "is-callable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
- "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI="
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
"dev": true,
- "requires": {
- "ci-info": "^2.0.0"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "is-color-stop": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
- "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
- "requires": {
- "css-color-names": "^0.0.4",
- "hex-color-regex": "^1.1.0",
- "hsl-regex": "^1.0.0",
- "hsla-regex": "^1.0.0",
- "rgb-regex": "^1.0.1",
- "rgba-regex": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
}
},
- "is-css-color": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-css-color/-/is-css-color-1.0.0.tgz",
- "integrity": "sha1-EQGYzd2xVTw5Nl4px1/btQIXC78="
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "node_modules/chai-as-promised": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.2.tgz",
+ "integrity": "sha512-1GadL+sEJVLzDjcawPM4kjfnL+p/9vrxiEUonowKOAzvVg0PixJUdtuDzdkDeQhK3zfOE76GqGkZIQ7/Adcrqw==",
"dev": true,
- "requires": {
- "kind-of": "^6.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true
- }
+ "check-error": "^2.1.1"
+ },
+ "peerDependencies": {
+ "chai": ">= 2.1.2 < 7"
}
},
- "is-date-object": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
- "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "node_modules/chai-datetime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/chai-datetime/-/chai-datetime-1.8.1.tgz",
+ "integrity": "sha512-GqQPq+l1yQI/c6sG2rvGkrM5SrmBm1ox/cmRjIA+g4632kLYGRWSIQR02qCeCkwabjYyCZNsHAKMV6gCsSV/1Q==",
"dev": true,
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- },
"dependencies": {
- "kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true
- }
+ "chai": ">1.9.0"
}
},
- "is-directory": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
- "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
- "dev": true
- },
- "is-docker": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz",
- "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==",
- "dev": true
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
- "dev": true
+ "node_modules/chai-subset": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz",
+ "integrity": "sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
},
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "node_modules/chai-things": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/chai-things/-/chai-things-0.2.0.tgz",
+ "integrity": "sha512-6ns0SU21xdRCoEXVKH3HGbwnsgfVMXQ+sU5V8PI9rfxaITos8lss1vUxbF1FAcJKjfqmmmLVlr/z3sLes00w+A==",
"dev": true
},
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
},
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
"dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
}
},
- "is-installed-globally": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
- "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
- "dev": true,
- "requires": {
- "global-dirs": "^0.1.0",
- "is-path-inside": "^1.0.0"
+ "node_modules/char-width-table-consumer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
+ "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
+ "dependencies": {
+ "binary-search": "^1.3.5"
}
},
- "is-invalid-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
- "integrity": "sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ=",
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
"dev": true,
- "requires": {
- "is-glob": "^2.0.0"
- },
- "dependencies": {
- "is-extglob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true
- },
- "is-glob": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
- "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
- "dev": true,
- "requires": {
- "is-extglob": "^1.0.0"
- }
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "is-npm": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz",
- "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==",
- "dev": true
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
},
- "is-number": {
+ "node_modules/character-entities-legacy": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
"dev": true,
- "requires": {
- "kind-of": "^3.0.2"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "is-number-object": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
- "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
- "dev": true
- },
- "is-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
- "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
- "dev": true
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
},
- "is-object": {
+ "node_modules/charset": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
- "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=",
- "dev": true
- },
- "is-observable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz",
- "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==",
+ "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz",
+ "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==",
"dev": true,
- "requires": {
- "symbol-observable": "^1.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
}
},
- "is-odd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz",
- "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==",
+ "node_modules/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
- "requires": {
- "is-number": "^4.0.0"
- },
- "dependencies": {
- "is-number": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
- "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
- "dev": true
- }
+ "engines": {
+ "node": ">= 16"
}
},
- "is-ok": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-ok/-/is-ok-1.0.1.tgz",
- "integrity": "sha1-AR/mBdvgeGF8zy89L6BNbAmUiQY="
- },
- "is-path-cwd": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
- "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
- "dev": true
- },
- "is-path-in-cwd": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
- "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+ "node_modules/check-more-types": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
+ "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
"dev": true,
- "requires": {
- "is-path-inside": "^2.1.0"
- },
- "dependencies": {
- "is-path-inside": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
- "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
- "dev": true,
- "requires": {
- "path-is-inside": "^1.0.2"
- }
- }
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "is-path-inside": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
- "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
- "dev": true,
- "requires": {
- "path-is-inside": "^1.0.1"
+ "node_modules/check-node-version": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/check-node-version/-/check-node-version-4.2.1.tgz",
+ "integrity": "sha512-YYmFYHV/X7kSJhuN/QYHUu998n/TRuDe8UenM3+m5NrkiH670lb9ILqHIvBencvJc4SDh+XcbXMR4b+TtubJiw==",
+ "dependencies": {
+ "chalk": "^3.0.0",
+ "map-values": "^1.0.1",
+ "minimist": "^1.2.0",
+ "object-filter": "^1.0.2",
+ "run-parallel": "^1.1.4",
+ "semver": "^6.3.0"
+ },
+ "bin": {
+ "check-node-version": "bin.js"
+ },
+ "engines": {
+ "node": ">=8.3.0"
}
},
- "is-plain-obj": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
- "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
- "dev": true
+ "node_modules/check-node-version/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
},
- "is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "dev": true,
- "requires": {
- "isobject": "^3.0.1"
+ "node_modules/check-node-version/node_modules/chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/check-node-version/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "is-png": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz",
- "integrity": "sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==",
- "dev": true
+ "node_modules/check-node-version/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
- "is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
- "dev": true
+ "node_modules/check-node-version/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
},
- "is-regex": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
- "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
- "requires": {
- "has": "^1.0.1"
+ "node_modules/check-node-version/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "is-regexp": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
- "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
+ "node_modules/check-types": {
+ "version": "11.2.2",
+ "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz",
+ "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==",
"dev": true
},
- "is-relative": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
- "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "node_modules/cheerio": {
+ "version": "1.0.0-rc.12",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+ "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
"dev": true,
- "requires": {
- "is-unc-path": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "htmlparser2": "^8.0.1",
+ "parse5": "^7.0.0",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
- "is-relative-url": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-3.0.0.tgz",
- "integrity": "sha512-U1iSYRlY2GIMGuZx7gezlB5dp1Kheaym7zKzO1PV06mOihiWTXejLwm4poEJysPyXF+HtK/BEd0DVlcCh30pEA==",
+ "node_modules/cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"dev": true,
- "requires": {
- "is-absolute-url": "^3.0.0"
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
}
},
- "is-resolvable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
- "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
- "dev": true
- },
- "is-retry-allowed": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
- "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
- "dev": true
- },
- "is-root": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz",
- "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==",
- "dev": true
- },
- "is-ssh": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz",
- "integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==",
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
- "requires": {
- "protocols": "^1.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
}
},
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
- "dev": true
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
},
- "is-string": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
- "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
- "dev": true
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0"
+ }
},
- "is-subset": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
- "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=",
- "dev": true
+ "node_modules/chunk-data": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/chunk-data/-/chunk-data-0.1.0.tgz",
+ "integrity": "sha512-zFyPtyC0SZ6Zu79b9sOYtXZcgrsXe0RpePrzRyj52hYVFG1+Rk6rBqjjOEk+GNQwc3PIX+86teQMok970pod1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "is-svg": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-4.2.1.tgz",
- "integrity": "sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A==",
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
- "requires": {
- "html-comment-regex": "^1.1.2"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
}
},
- "is-symbol": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
- "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI="
- },
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ "node_modules/cjs-module-lexer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz",
+ "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==",
+ "license": "MIT"
},
- "is-unc-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
- "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "node_modules/clean-css": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+ "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dev": true,
- "requires": {
- "unc-path-regex": "^0.1.2"
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
}
},
- "is-valid-path": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
- "integrity": "sha1-EQ+f90w39mPh7HkV60UfLbk6yd8=",
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true,
- "requires": {
- "is-invalid-path": "^0.1.0"
+ "engines": {
+ "node": ">=6"
}
},
- "is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "dev": true
- },
- "is-wsl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
- "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
- "dev": true
- },
- "is-yarn-global": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
- "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
- "dev": true
- },
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
- "dev": true
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- },
- "isstream": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
- },
- "istanbul-lib-coverage": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
- "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
- "dev": true
- },
- "istanbul-lib-hook": {
+ "node_modules/cli-boxes": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
- "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
"dev": true,
- "requires": {
- "append-transform": "^2.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "istanbul-lib-instrument": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz",
- "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.7.5",
- "@babel/parser": "^7.7.5",
- "@babel/template": "^7.7.4",
- "@babel/traverse": "^7.7.4",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
- },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "istanbul-lib-processinfo": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz",
- "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==",
+ "node_modules/cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
"dev": true,
- "requires": {
- "archy": "^1.0.0",
- "cross-spawn": "^7.0.0",
- "istanbul-lib-coverage": "^3.0.0-alpha.1",
- "make-dir": "^3.0.0",
- "p-map": "^3.0.0",
- "rimraf": "^3.0.0",
- "uuid": "^3.3.3"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
- "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
- "dev": true,
- "requires": {
- "aggregate-error": "^3.0.0"
- }
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
}
},
- "istanbul-lib-report": {
+ "node_modules/cli-table3/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cli-table3/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- },
- "dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
}
},
- "istanbul-lib-source-maps": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz",
- "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==",
+ "node_modules/cli-table3/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "istanbul-reports": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
- "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
+ "node_modules/cli-truncate": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+ "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
"dev": true,
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^8.0.0",
+ "string-width": "^8.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "isurl": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz",
- "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==",
+ "node_modules/cli-truncate/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
- "requires": {
- "has-to-string-tag-x": "^1.2.0",
- "is-object": "^1.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "iterall": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz",
- "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg=="
- },
- "iterate-object": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.3.tgz",
- "integrity": "sha512-DximWbkke36cnrSfNJv6bgcB2QOMV9PRD2FiowwzCoMsh8RupFLdbNIzWe+cVDWT+NIMNJgGlB1dGxP6kpzGtA=="
- },
- "its-name": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/its-name/-/its-name-1.0.0.tgz",
- "integrity": "sha1-IGXxiD7LVoxl9xEt2/EjQB+uSvA=",
- "dev": true
- },
- "jest-worker": {
- "version": "24.9.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz",
- "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==",
+ "node_modules/cli-truncate/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
- "requires": {
- "merge-stream": "^2.0.0",
- "supports-color": "^6.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
},
- "dependencies": {
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "joi-extension-semver": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/joi-extension-semver/-/joi-extension-semver-4.0.0.tgz",
- "integrity": "sha512-s9oWm85FNaGB1flGe/KGK9F1xBZHVGFwhsVy4BF7S8pHe5R078Y1gZu4DHOmreIz/zVCPAg5oNL5ZOH6Va22uA==",
- "requires": {
- "semver": "^6.1.1"
+ "node_modules/cli-truncate/node_modules/slice-ansi": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+ "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.3",
+ "is-fullwidth-code-point": "^5.1.0"
},
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
+ "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
- }
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "js-tokens": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
- "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
- "dev": true
- },
- "js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- },
+ "node_modules/cli-truncate/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
- }
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "js-yaml-loader": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/js-yaml-loader/-/js-yaml-loader-1.2.2.tgz",
- "integrity": "sha512-H+NeuNrG6uOs/WMjna2SjkaCw13rMWiT/D7l9+9x5n8aq88BDsh2sRmdfxckWPIHtViYHWRG6XiCKYvS1dfyLg==",
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
- "requires": {
- "js-yaml": "^3.13.1",
- "loader-utils": "^1.2.3",
- "un-eval": "^1.2.0"
- },
+ "license": "ISC",
"dependencies": {
- "big.js": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
- "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
- "dev": true
- },
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
- "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^2.0.0",
- "json5": "^1.0.1"
- }
- }
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "js2xmlparser": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz",
- "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==",
+ "node_modules/cliui/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
- "xmlcreate": "^2.0.3"
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "jsbn": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
- },
- "jsdoc": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.4.tgz",
- "integrity": "sha512-3G9d37VHv7MFdheviDCjUfQoIjdv4TC5zTTf5G9VODLtOnVS6La1eoYBDlbWfsRT3/Xo+j2MIqki2EV12BZfwA==",
+ "node_modules/cliui/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
- "requires": {
- "@babel/parser": "^7.9.4",
- "bluebird": "^3.7.2",
- "catharsis": "^0.8.11",
- "escape-string-regexp": "^2.0.0",
- "js2xmlparser": "^4.0.1",
- "klaw": "^3.0.0",
- "markdown-it": "^10.0.0",
- "markdown-it-anchor": "^5.2.7",
- "marked": "^0.8.2",
- "mkdirp": "^1.0.4",
- "requizzle": "^0.2.3",
- "strip-json-comments": "^3.1.0",
- "taffydb": "2.6.2",
- "underscore": "~1.10.2"
- },
+ "license": "MIT",
"dependencies": {
- "@babel/parser": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz",
- "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==",
- "dev": true
- },
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true
- },
- "strip-json-comments": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
- "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
- "dev": true
- },
- "underscore": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
- "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
- "dev": true
- }
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "jsdoctypeparser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz",
- "integrity": "sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA==",
- "dev": true
+ "node_modules/cliui/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
},
- "jsesc": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
- "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=",
- "dev": true
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
},
- "json-buffer": {
+ "node_modules/cliui/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
- "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
- "dev": true
- },
- "json-loader": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz",
- "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==",
- "dev": true
- },
- "json-parse-better-errors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
- "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
- "dev": true
- },
- "json-schema": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
- },
- "json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
- },
- "json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
- "dev": true
- },
- "json-stringify-safe": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
- },
- "json3": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
- "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
- "dev": true
- },
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
- "requires": {
- "minimist": "^1.2.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
}
},
- "jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.6"
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "jsonify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
- "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
- "dev": true
- },
- "jsonpath": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.0.2.tgz",
- "integrity": "sha512-rmzlgFZiQPc6q4HDyK8s9Qb4oxBnI5sF61y/Co5PV0lc3q2bIuRsNdueVbhoSHdKM4fxeimphOAtfz47yjCfeA==",
- "requires": {
- "esprima": "1.2.2",
- "static-eval": "2.0.2",
- "underscore": "1.7.0"
- },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "esprima": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz",
- "integrity": "sha1-dqD9Zvz+FU/SkmZ9wmQBl1CxZXs="
- }
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "jsonpointer": {
+ "node_modules/clone-deep": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
- "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
- "dev": true
- },
- "jsonwebtoken": {
- "version": "8.5.1",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
- "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
"dev": true,
- "requires": {
- "jws": "^3.2.2",
- "lodash.includes": "^4.3.0",
- "lodash.isboolean": "^3.0.3",
- "lodash.isinteger": "^4.0.4",
- "lodash.isnumber": "^3.0.3",
- "lodash.isplainobject": "^4.0.6",
- "lodash.isstring": "^4.0.1",
- "lodash.once": "^4.0.0",
- "ms": "^2.1.1",
- "semver": "^5.6.0"
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
},
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/clone-deep/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
"dependencies": {
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "jsprim": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
- "requires": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.3.0",
- "json-schema": "0.2.3",
- "verror": "1.10.0"
+ "node_modules/cloudflare-middleware": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cloudflare-middleware/-/cloudflare-middleware-1.0.4.tgz",
+ "integrity": "sha512-KZStJ6PvB2bc/wThCaEBvtVLq5Sz/Z2CtBPrlwyqws51Dr1JOCQPSCgTzQ8gSydzcDhO1VK4ioGUQU3+02TR7A==",
+ "dependencies": {
+ "range_check": "^1.4.0"
}
},
- "jsx-ast-utils": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
- "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==",
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"dev": true,
- "requires": {
- "array-includes": "^3.0.3",
- "object.assign": "^4.1.0"
+ "engines": {
+ "node": ">=6"
}
},
- "just-extend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz",
- "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==",
- "dev": true
- },
- "jwa": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
- "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "node_modules/collapse-white-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
+ "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==",
"dev": true,
- "requires": {
- "buffer-equal-constant-time": "1.0.1",
- "ecdsa-sig-formatter": "1.0.11",
- "safe-buffer": "^5.0.1"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "jws": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
- "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
- "requires": {
- "jwa": "^1.4.1",
- "safe-buffer": "^5.0.1"
+ "dependencies": {
+ "color-name": "1.1.3"
}
},
- "keygrip": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz",
- "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g=="
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
},
- "keyv": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
- "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true,
- "requires": {
- "json-buffer": "3.0.0"
- }
+ "license": "MIT"
},
- "killable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
- "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "node_modules/colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
+ "engines": {
+ "node": ">=0.1.90"
}
},
- "klaw": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
- "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "node_modules/combine-promises": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz",
+ "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.9"
+ "engines": {
+ "node": ">=10"
}
},
- "kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "dev": true
- },
- "ky": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/ky/-/ky-0.12.0.tgz",
- "integrity": "sha512-t9b7v3V2fGwAcQnnDDQwKQGF55eWrf4pwi1RN08Fy8b/9GEwV7Ea0xQiaSW6ZbeghBHIwl8kgnla4vVo9seepQ==",
- "dev": true
- },
- "ky-universal": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.3.0.tgz",
- "integrity": "sha512-CM4Bgb2zZZpsprcjI6DNYTaH3oGHXL2u7BU4DK+lfCuC4snkt9/WRpMYeKbBbXscvKkeqBwzzjFX2WwmKY5K/A==",
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
- "requires": {
- "abort-controller": "^3.0.0",
- "node-fetch": "^2.6.0"
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "last-call-webpack-plugin": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz",
- "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==",
+ "node_modules/comlink": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz",
+ "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==",
+ "dev": true
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
"dev": true,
- "requires": {
- "lodash": "^4.17.5",
- "webpack-sources": "^1.1.0"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "latest-version": {
+ "node_modules/commander": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
- "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
"dev": true,
- "requires": {
- "package-json": "^6.3.0"
+ "engines": {
+ "node": ">= 6"
}
},
- "lazy-ass": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
- "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=",
- "dev": true
- },
- "lcid": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
- "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "node_modules/comment-parser": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.7.tgz",
+ "integrity": "sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==",
"dev": true,
- "requires": {
- "invert-kv": "^2.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.0.0"
}
},
- "leven": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
- "dev": true
+ "node_modules/common-path-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
+ "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
+ "dev": true,
+ "license": "ISC"
},
- "levenary": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz",
- "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==",
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"dev": true,
- "requires": {
- "leven": "^3.1.0"
+ "engines": {
+ "node": ">=4.0.0"
}
},
- "levn": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
- "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
- "requires": {
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2"
- }
+ "node_modules/component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
},
- "li": {
+ "node_modules/component-emitter": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz",
- "integrity": "sha1-IsWbyu+qmo7zWc91l4TkvxBq6hs=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
- "lines-and-columns": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
- "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
- "dev": true
+ "node_modules/component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
},
- "linkify-it": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
- "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+ "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"dev": true,
- "requires": {
- "uc.micro": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "compressible": "~2.0.18",
+ "debug": "2.6.9",
+ "negotiator": "~0.6.4",
+ "on-headers": "~1.1.0",
+ "safe-buffer": "5.2.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "lint-staged": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-9.5.0.tgz",
- "integrity": "sha512-nawMob9cb/G1J98nb8v3VC/E8rcX1rryUYXVZ69aT9kde6YWX+uvNOEHY5yf2gcWcTJGiD0kqXmCnS3oD75GIA==",
+ "node_modules/compression/node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
- "requires": {
- "chalk": "^2.4.2",
- "commander": "^2.20.0",
- "cosmiconfig": "^5.2.1",
- "debug": "^4.1.1",
- "dedent": "^0.7.0",
- "del": "^5.0.0",
- "execa": "^2.0.3",
- "listr": "^0.14.3",
- "log-symbols": "^3.0.0",
- "micromatch": "^4.0.2",
- "normalize-path": "^3.0.0",
- "please-upgrade-node": "^3.1.1",
- "string-argv": "^0.3.0",
- "stringify-object": "^3.3.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- },
- "cross-spawn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
- "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "execa": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz",
- "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^3.0.0",
- "onetime": "^5.1.0",
- "p-finally": "^2.0.0",
- "signal-exit": "^3.0.2",
- "strip-final-newline": "^2.0.0"
- }
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "get-stream": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
- "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
- },
- "log-symbols": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.2"
- }
- },
- "micromatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
- "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
- "dev": true,
- "requires": {
- "braces": "^3.0.1",
- "picomatch": "^2.0.5"
- }
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "npm-run-path": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz",
- "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
- "onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "p-finally": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
- "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
- "dev": true
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "listr": {
- "version": "0.14.3",
- "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
- "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
- "requires": {
- "@samverschueren/stream-to-observable": "^0.3.0",
- "is-observable": "^1.1.0",
- "is-promise": "^2.1.0",
- "is-stream": "^1.1.0",
- "listr-silent-renderer": "^1.1.1",
- "listr-update-renderer": "^0.5.0",
- "listr-verbose-renderer": "^0.5.0",
- "p-map": "^2.0.0",
- "rxjs": "^6.3.3"
- },
+ "license": "MIT",
"dependencies": {
- "p-map": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
- "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
- "dev": true
- }
+ "ms": "2.0.0"
}
},
- "listr-silent-renderer": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
- "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=",
- "dev": true
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
},
- "listr-update-renderer": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz",
- "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
- "dev": true,
- "requires": {
- "chalk": "^1.1.3",
- "cli-truncate": "^0.2.1",
- "elegant-spinner": "^1.0.1",
- "figures": "^1.7.0",
- "indent-string": "^3.0.0",
- "log-symbols": "^1.0.2",
- "log-update": "^2.3.0",
- "strip-ansi": "^3.0.1"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "figures": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5",
- "object-assign": "^4.1.0"
- }
- },
- "log-symbols": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
- "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
- "dev": true,
- "requires": {
- "chalk": "^1.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- }
+ "node_modules/compression/node_modules/negotiator": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+ "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
}
},
- "listr-verbose-renderer": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz",
- "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
+ "node_modules/compute-gcd": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz",
+ "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==",
"dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "cli-cursor": "^2.1.0",
- "date-fns": "^1.27.2",
- "figures": "^2.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
+ "dependencies": {
+ "validate.io-array": "^1.0.3",
+ "validate.io-function": "^1.0.2",
+ "validate.io-integer-array": "^1.0.0"
}
},
- "load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "node_modules/compute-lcm": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz",
+ "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "strip-bom": "^3.0.0"
- }
- },
- "loader-fs-cache": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz",
- "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==",
- "dev": true,
- "requires": {
- "find-cache-dir": "^0.1.1",
- "mkdirp": "0.5.1"
- },
- "dependencies": {
- "find-cache-dir": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz",
- "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "mkdirp": "^0.5.1",
- "pkg-dir": "^1.0.0"
- }
- },
- "find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
- "dev": true,
- "requires": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
- "path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
- "dev": true,
- "requires": {
- "pinkie-promise": "^2.0.0"
- }
- },
- "pkg-dir": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
- "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
- "dev": true,
- "requires": {
- "find-up": "^1.0.0"
- }
- }
+ "dependencies": {
+ "compute-gcd": "^1.2.1",
+ "validate.io-array": "^1.0.3",
+ "validate.io-function": "^1.0.2",
+ "validate.io-integer-array": "^1.0.0"
}
},
- "loader-runner": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
- "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
- "loader-utils": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
- "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "node_modules/concurrently": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.3.tgz",
+ "integrity": "sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA==",
"dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^2.0.0",
- "json5": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "5.6.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.8.4",
+ "supports-color": "10.2.2",
+ "tree-kill": "1.2.2",
+ "yargs": "18.0.0"
+ },
+ "bin": {
+ "conc": "dist/bin/index.js",
+ "concurrently": "dist/bin/index.js"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
- "locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "node_modules/concurrently/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
- "requires": {
- "p-locate": "^2.0.0",
- "path-exists": "^3.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
},
- "dependencies": {
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "lockfile": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
- "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
+ "node_modules/concurrently/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
- "requires": {
- "signal-exit": "^3.0.2"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
- },
- "lodash.at": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
- "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=",
- "dev": true
+ "node_modules/concurrently/node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
},
- "lodash.clonedeep": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
- "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
- "dev": true
+ "node_modules/concurrently/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
},
- "lodash.concat": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.concat/-/lodash.concat-4.5.0.tgz",
- "integrity": "sha1-sFOuAuSoAIWC5yVrnQK9ptA4A5U=",
- "dev": true
+ "node_modules/concurrently/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.countby": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.countby/-/lodash.countby-4.6.0.tgz",
- "integrity": "sha1-U1HyTeFnJKAFm1YfkgsNgK94ozw="
+ "node_modules/concurrently/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
},
- "lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
- "dev": true
+ "node_modules/concurrently/node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
},
- "lodash.defaults": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
- "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+ "node_modules/concurrently/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
},
- "lodash.difference": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
- "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=",
- "dev": true
+ "node_modules/concurrently/node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
},
- "lodash.escape": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
- "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=",
- "dev": true
+ "node_modules/concurrently/node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
},
- "lodash.every": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.every/-/lodash.every-4.6.0.tgz",
- "integrity": "sha1-64mYS+vENkJ5uzrvu9HKGb+mxqc=",
- "dev": true
+ "node_modules/config": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/config/-/config-4.4.2.tgz",
+ "integrity": "sha512-5HJD7TX70gOG2dAd72eDjDeSTYn6D8nOiKgysQQS8mRUo9X1wDtlzb1JlmIWkP+sM9zKRVdYJxmj+qmCsYu6DQ==",
+ "license": "MIT",
+ "dependencies": {
+ "json5": "^2.2.3"
+ },
+ "engines": {
+ "node": ">= 20.0.0"
+ }
},
- "lodash.filter": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
- "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=",
- "dev": true
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
},
- "lodash.find": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz",
- "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=",
- "dev": true
+ "node_modules/config-chain/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
},
- "lodash.flatten": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
- "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+ "node_modules/configstore": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz",
+ "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dot-prop": "^6.0.1",
+ "graceful-fs": "^4.2.6",
+ "unique-string": "^3.0.0",
+ "write-file-atomic": "^3.0.3",
+ "xdg-basedir": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/yeoman/configstore?sponsor=1"
+ }
},
- "lodash.flattendeep": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
- "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
- "dev": true
+ "node_modules/connect-history-api-fallback": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+ "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
},
- "lodash.foreach": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
- "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=",
- "dev": true
+ "node_modules/consola": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz",
+ "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.10.0"
+ }
},
- "lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
- "dev": true
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "lodash.groupby": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
- "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "lodash.includes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
- "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=",
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
},
- "lodash.isboolean": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
- "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=",
- "dev": true
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "lodash.isequal": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
- "dev": true
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "dev": true,
+ "license": "MIT"
},
- "lodash.isfunction": {
- "version": "3.0.9",
- "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
- "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
- "dev": true
+ "node_modules/cookiejar": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+ "dev": true,
+ "license": "MIT"
},
- "lodash.isinteger": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
- "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=",
- "dev": true
+ "node_modules/cookies": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz",
+ "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "keygrip": "~1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "lodash.isnumber": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
- "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=",
- "dev": true
+ "node_modules/copy-text-to-clipboard": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz",
+ "integrity": "sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.isobject": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
- "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=",
- "dev": true
+ "node_modules/copy-webpack-plugin": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
+ "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "^3.2.11",
+ "glob-parent": "^6.0.1",
+ "globby": "^13.1.1",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^4.0.0",
+ "serialize-javascript": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ }
},
- "lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
- "dev": true
+ "node_modules/copy-webpack-plugin/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
},
- "lodash.isstring": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
- "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=",
- "dev": true
+ "node_modules/copy-webpack-plugin/node_modules/globby": {
+ "version": "13.2.2",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz",
+ "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.3.0",
+ "ignore": "^5.2.4",
+ "merge2": "^1.4.1",
+ "slash": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.isundefined": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
- "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=",
- "dev": true
+ "node_modules/copy-webpack-plugin/node_modules/slash": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
+ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz",
- "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=",
- "dev": true
+ "node_modules/core-js": {
+ "version": "3.36.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz",
+ "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
},
- "lodash.map": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
- "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
- "dev": true
+ "node_modules/core-js-compat": {
+ "version": "3.45.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
+ "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.25.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
},
- "lodash.mapvalues": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
- "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=",
- "dev": true
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
- "lodash.maxby": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.maxby/-/lodash.maxby-4.6.0.tgz",
- "integrity": "sha1-CCJABo88eiJ6oAqDgOTzjPB4bj0=",
- "dev": true
+ "node_modules/cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "dependencies": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
},
- "lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
- "dev": true
+ "node_modules/cosmiconfig/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
},
- "lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=",
- "dev": true
+ "node_modules/cross-env": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
+ "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@epic-web/invariant": "^1.0.0",
+ "cross-spawn": "^7.0.6"
+ },
+ "bin": {
+ "cross-env": "dist/bin/cross-env.js",
+ "cross-env-shell": "dist/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
},
- "lodash.set": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
- "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=",
- "dev": true
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
- "dev": true
+ "node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
- "lodash.throttle": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
- "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"dev": true,
- "optional": true
+ "license": "MIT"
},
- "lodash.times": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/lodash.times/-/lodash.times-4.3.2.tgz",
- "integrity": "sha1-Ph8lZcQxdU1Uq1fy7RdBk5KFyh0="
+ "node_modules/crypto-random-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.toarray": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
- "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=",
- "dev": true
+ "node_modules/crypto-random-string/node_modules/type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+ "node_modules/css-blank-pseudo": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz",
+ "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/css-color-converter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz",
+ "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==",
+ "dependencies": {
+ "color-convert": "^0.5.2",
+ "color-name": "^1.1.4",
+ "css-unit-converter": "^1.1.2"
+ }
+ },
+ "node_modules/css-color-converter/node_modules/color-convert": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
+ "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling=="
+ },
+ "node_modules/css-color-converter/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/css-declaration-sorter": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz",
+ "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/css-has-pseudo": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz",
+ "integrity": "sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
+ "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/css-loader": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
+ "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.33",
+ "postcss-modules-extract-imports": "^3.1.0",
+ "postcss-modules-local-by-default": "^4.0.5",
+ "postcss-modules-scope": "^3.2.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/css-minimizer-webpack-plugin": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz",
+ "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "cssnano": "^6.0.1",
+ "jest-worker": "^29.4.3",
+ "postcss": "^8.4.24",
+ "schema-utils": "^4.0.1",
+ "serialize-javascript": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@parcel/css": {
+ "optional": true
+ },
+ "@swc/css": {
+ "optional": true
+ },
+ "clean-css": {
+ "optional": true
+ },
+ "csso": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/css-prefers-color-scheme": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz",
+ "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+ "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-unit-converter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
+ "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssdb": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.9.0.tgz",
+ "integrity": "sha512-J8jOU/hLjaXcO1LldOLraJSQpfLXRKof0I7mtbRyOy2AAXgqst0x9rlgi2qXeD6d0ou3ZLqcPAMqYVbpCbrxEw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ }
+ ],
+ "license": "MIT-0"
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssnano": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
+ "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssnano-preset-default": "^6.1.2",
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cssnano"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-preset-advanced": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz",
+ "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "autoprefixer": "^10.4.19",
+ "browserslist": "^4.23.0",
+ "cssnano-preset-default": "^6.1.2",
+ "postcss-discard-unused": "^6.0.5",
+ "postcss-merge-idents": "^6.0.3",
+ "postcss-reduce-idents": "^6.0.3",
+ "postcss-zindex": "^6.0.2"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-preset-default": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
+ "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "css-declaration-sorter": "^7.2.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-calc": "^9.0.1",
+ "postcss-colormin": "^6.1.0",
+ "postcss-convert-values": "^6.1.0",
+ "postcss-discard-comments": "^6.0.2",
+ "postcss-discard-duplicates": "^6.0.3",
+ "postcss-discard-empty": "^6.0.3",
+ "postcss-discard-overridden": "^6.0.2",
+ "postcss-merge-longhand": "^6.0.5",
+ "postcss-merge-rules": "^6.1.1",
+ "postcss-minify-font-values": "^6.1.0",
+ "postcss-minify-gradients": "^6.0.3",
+ "postcss-minify-params": "^6.1.0",
+ "postcss-minify-selectors": "^6.0.4",
+ "postcss-normalize-charset": "^6.0.2",
+ "postcss-normalize-display-values": "^6.0.2",
+ "postcss-normalize-positions": "^6.0.2",
+ "postcss-normalize-repeat-style": "^6.0.2",
+ "postcss-normalize-string": "^6.0.2",
+ "postcss-normalize-timing-functions": "^6.0.2",
+ "postcss-normalize-unicode": "^6.1.0",
+ "postcss-normalize-url": "^6.0.2",
+ "postcss-normalize-whitespace": "^6.0.2",
+ "postcss-ordered-values": "^6.0.2",
+ "postcss-reduce-initial": "^6.1.0",
+ "postcss-reduce-transforms": "^6.0.2",
+ "postcss-svgo": "^6.0.3",
+ "postcss-unique-selectors": "^6.0.4"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-utils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
+ "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "dev": true,
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "dev": true
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true
+ },
+ "node_modules/cypress": {
+ "version": "15.19.0",
+ "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.19.0.tgz",
+ "integrity": "sha512-kjy1u3SWlMRWS5qffH3U+bXx1PqPP7qwhIzEY3UtERcW2r/8zy5uk6uSwa75pYXJyYFVP1SO8mAn/avZUvUbfg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cypress/request": "^4.0.0",
+ "@cypress/xvfb": "^1.2.4",
+ "@types/sinonjs__fake-timers": "8.1.1",
+ "@types/sizzle": "^2.3.2",
+ "@types/tmp": "^0.2.3",
+ "arch": "^2.2.0",
+ "blob-util": "^2.0.2",
+ "bluebird": "^3.7.2",
+ "buffer": "^5.7.1",
+ "cachedir": "^2.4.0",
+ "chalk": "^4.1.0",
+ "ci-info": "^4.1.0",
+ "cli-table3": "0.6.1",
+ "commander": "^6.2.1",
+ "common-tags": "^1.8.0",
+ "dayjs": "^1.10.4",
+ "debug": "^4.3.4",
+ "eventemitter2": "6.4.7",
+ "execa": "4.1.0",
+ "executable": "^4.1.1",
+ "fs-extra": "^9.1.0",
+ "hasha": "5.2.2",
+ "is-installed-globally": "~0.4.0",
+ "listr2": "^9.0.5",
+ "lodash": "^4.17.23",
+ "log-symbols": "^4.0.0",
+ "minimist": "^1.2.8",
+ "ospath": "^1.2.2",
+ "pretty-bytes": "^5.6.0",
+ "process": "^0.11.10",
+ "proxy-from-env": "1.0.0",
+ "request-progress": "^3.0.0",
+ "supports-color": "^8.1.1",
+ "systeminformation": "^5.31.1",
+ "tmp": "~0.2.4",
+ "tree-kill": "1.2.2",
+ "tslib": "1.14.1",
+ "untildify": "^4.0.0",
+ "yauzl": "^3.3.1"
+ },
+ "bin": {
+ "cypress": "bin/cypress"
+ },
+ "engines": {
+ "node": "^20.1.0 || ^22.0.0 || >=24.0.0"
+ }
+ },
+ "node_modules/cypress-wait-for-stable-dom": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/cypress-wait-for-stable-dom/-/cypress-wait-for-stable-dom-0.1.0.tgz",
+ "integrity": "sha512-iVJc6CDzlu1xUnTcZph+zbkOlImaDelpvRv4G+3naugvjkF6b9EFpDmRCC/16xL1pqpkFq4rFyfhuNw4C3PQjw==",
+ "dev": true
+ },
+ "node_modules/cypress/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/ci-info": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
+ "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/cli-table3": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz",
+ "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "colors": "1.4.0"
+ }
+ },
+ "node_modules/cypress/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cypress/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "log-symbols": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "node_modules/cypress/node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cypress/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cypress/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress/node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cypress/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/danger": {
+ "version": "13.0.10",
+ "resolved": "https://registry.npmjs.org/danger/-/danger-13.0.10.tgz",
+ "integrity": "sha512-qMa/YdfVP/a1HhuvMUsocTO0otgLphuiV5z4G5P7C4+qHvNtCSlzvZm3UuwBut2iwQ/r5NGcwcmD4t+8LjIS0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@gitbeaker/rest": "^38.0.0",
+ "@octokit/rest": "^20.1.2",
+ "async-retry": "1.2.3",
+ "chalk": "^4.1.2",
+ "commander": "^2.18.0",
+ "core-js": "^3.8.2",
+ "debug": "^4.1.1",
+ "fast-json-patch": "^3.0.0-1",
+ "hyperlinker": "^1.0.0",
+ "ini": "^5.0.0",
+ "json5": "^2.2.3",
+ "jsonpointer": "^5.0.0",
+ "jsonwebtoken": "^9.0.0",
+ "lodash.includes": "^4.3.0",
+ "lodash.isobject": "^3.0.2",
+ "lodash.mapvalues": "^4.6.0",
+ "lodash.memoize": "^4.1.2",
+ "memfs-or-file-map-to-github-branch": "^1.3.0",
+ "micromatch": "^4.0.4",
+ "node-cleanup": "^2.1.2",
+ "override-require": "^1.1.1",
+ "parse-diff": "^0.7.0",
+ "parse-github-url": "^1.0.2",
+ "parse-link-header": "^2.0.0",
+ "pinpoint": "^1.1.0",
+ "prettyjson": "^1.2.1",
+ "readline-sync": "^1.4.9",
+ "regenerator-runtime": "^0.13.9",
+ "require-from-string": "^2.0.2",
+ "supports-hyperlinks": "^4.3.0",
+ "undici": "6.21.1"
+ },
+ "bin": {
+ "danger": "distribution/commands/danger.js",
+ "danger-ci": "distribution/commands/danger-ci.js",
+ "danger-init": "distribution/commands/danger-init.js",
+ "danger-js": "distribution/commands/danger.js",
+ "danger-local": "distribution/commands/danger-local.js",
+ "danger-pr": "distribution/commands/danger-pr.js",
+ "danger-process": "distribution/commands/danger-process.js",
+ "danger-reset-status": "distribution/commands/danger-reset-status.js",
+ "danger-runner": "distribution/commands/danger-runner.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/danger/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/danger/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/danger/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/danger/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/danger/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/danger/node_modules/ini": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz",
+ "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/danger/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/danger/node_modules/undici": {
+ "version": "6.21.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
+ "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
+ "node_modules/debounce": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz",
+ "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decamelize-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz",
+ "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==",
+ "dev": true,
+ "dependencies": {
+ "decamelize": "^1.1.0",
+ "map-obj": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decamelize-keys/node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decamelize-keys/node_modules/map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
+ "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
+ "dev": true,
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/default-browser": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
+ "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
+ "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/deprecation": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
+ },
+ "node_modules/detect-package-manager": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-3.0.2.tgz",
+ "integrity": "sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "execa": "^5.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/detect-package-manager/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/detect-package-manager/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/detect-package-manager/node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/detect-port": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-2.1.0.tgz",
+ "integrity": "sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "address": "^2.0.1"
+ },
+ "bin": {
+ "detect": "dist/commonjs/bin/detect-port.js",
+ "detect-port": "dist/commonjs/bin/detect-port.js"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/diff": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz",
+ "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.4.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
+ "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/disparity": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/disparity/-/disparity-3.0.0.tgz",
+ "integrity": "sha512-n94Rzbv2ambRaFzrnBf34IEiyOdIci7maRpMkoQWB6xFYGA7Nbs0Z5YQzMfTeyQeelv23nayqOcssBoc6rKrgw==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "diff": "^4.0.1"
+ },
+ "bin": {
+ "disparity": "bin/disparity"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/disparity/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/disparity/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/disparity/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/disparity/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dns-packet": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
+ "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@leichtgewicht/ip-codec": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.8.0.tgz",
+ "integrity": "sha512-49uwh41eyY+C+0MSXlg8/t0g0mRFOZWN58Hfq4X8607Sek4iTrdMXDNyV1PP0Cor5i+RlG3fmP10Sot7Zaf7mQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/mdx-loader": "^3.7.0",
+ "@docusaurus/plugin-content-docs": "^3.7.0",
+ "@docusaurus/utils": "^3.7.0",
+ "@docusaurus/utils-common": "^3.7.0",
+ "@docusaurus/utils-validation": "^3.7.0",
+ "chalk": "^4.1.2",
+ "clsx": "^1.2.1",
+ "js-yaml": "^4.1.0",
+ "json-refs": "^3.0.15",
+ "json-schema-resolve-allof": "^1.5.0",
+ "lodash": "^4.17.20",
+ "openapi-to-postmanv2": "^4.20.1",
+ "postman-collection": "^4.1.0",
+ "webpack": "^5.95.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/docusaurus-plugin-openapi/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/docusaurus-plugin-proxy": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/docusaurus-plugin-proxy/-/docusaurus-plugin-proxy-0.8.0.tgz",
+ "integrity": "sha512-UXZhTkyR+5SQKKDkoorOD6UDwaWvR2ngpJ3u4bFR/irdBo7xcm6VmBXrM1xkdl/nqysu/qUZwTUU1ggy876Lrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/docusaurus-preset-openapi": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/docusaurus-preset-openapi/-/docusaurus-preset-openapi-0.8.0.tgz",
+ "integrity": "sha512-6n2XyzZBxr9l5BgiZb2h6mMPWbp3ppJ0hBvq03KTurBFxo5hT7Ssi0mp0MeU4kDkBt/aqHes2wDSPOtrUWQUEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/preset-classic": "^3.8.0",
+ "docusaurus-plugin-openapi": "^0.8.0",
+ "docusaurus-plugin-proxy": "^0.8.0",
+ "docusaurus-theme-openapi": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/docusaurus-theme-openapi/-/docusaurus-theme-openapi-0.8.0.tgz",
+ "integrity": "sha512-Tx5WUKSwYNVysZV5QxyUnSPJfMmnJwBM0ks51+45pFKziYMZa92j6d6Cx1Bv0e1kxnFOadWpP2CRmuN3CjP7PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docusaurus/theme-common": "^3.7.0",
+ "@mdx-js/react": "^3.0.0",
+ "@monaco-editor/react": "^4.7.0",
+ "@reduxjs/toolkit": "^2.4.0",
+ "buffer": "^6.0.3",
+ "clsx": "^1.2.1",
+ "crypto-js": "^4.1.1",
+ "docusaurus-plugin-openapi": "^0.8.0",
+ "immer": "^9.0.7",
+ "lodash": "^4.17.20",
+ "marked": "^11.0.0",
+ "monaco-editor": "^0.31.1",
+ "postman-code-generators": "^1.0.0",
+ "postman-collection": "^4.1.0",
+ "prism-react-renderer": "^2.1.0",
+ "process": "^0.11.10",
+ "react-magic-dropzone": "^1.0.1",
+ "react-redux": "^9.0.0",
+ "redux-devtools-extension": "^2.13.8",
+ "refractor": "^4.8.1",
+ "striptags": "^3.2.0",
+ "webpack": "^5.95.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi/node_modules/marked": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz",
+ "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi/node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/docusaurus-theme-openapi/node_modules/redux-devtools-extension": {
+ "version": "2.13.9",
+ "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz",
+ "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==",
+ "deprecated": "Package moved to @redux-devtools/extension.",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "redux": "^3.1.0 || ^4.0.0"
+ }
+ },
+ "node_modules/dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "utila": "~0.4"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+ "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dot-prop/node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.395",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz",
+ "integrity": "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true
+ },
+ "node_modules/emojic": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/emojic/-/emojic-1.1.18.tgz",
+ "integrity": "sha512-ZUOBdXQzt0dqtjVNW1GcBAwGiORydDs7+z/sYBK8CazmJUcdCwdDLkGj1FjL8d6TG+F1ZtmiXXQ2+cC9tHE68w==",
+ "license": "MIT",
+ "dependencies": {
+ "camelo": "^1.2.1",
+ "emojilib": "^2.0.2",
+ "iterate-object": "^1.2.0",
+ "r-json": "^1.1.0"
+ }
+ },
+ "node_modules/emojilib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
+ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw=="
+ },
+ "node_modules/emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/emoticon": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz",
+ "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding-sniffer": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz",
+ "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==",
+ "dev": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.3",
+ "whatwg-encoding": "^3.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.2.tgz",
+ "integrity": "sha512-C4JjGQZLY3kWlIDx0BQNKizbrfpb7NahxDztGdN5jrPK2ghmXiNDN+E/t0JzDeNRZxPVaszxEng42Pmj27X/0w==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "debug": "~4.1.0",
+ "engine.io-parser": "~2.2.0",
+ "ws": "~7.5.10"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/engine.io-client": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.4.tgz",
+ "integrity": "sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g==",
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "~1.3.0",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.2.0",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.6",
+ "parseuri": "0.0.6",
+ "ws": "~7.5.10",
+ "xmlhttprequest-ssl": "~1.6.2",
+ "yeast": "0.1.2"
+ }
+ },
+ "node_modules/engine.io-client/node_modules/debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/engine.io-client/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/engine.io-client/node_modules/ws": {
+ "version": "7.5.12",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.12.tgz",
+ "integrity": "sha512-1xGnbYN3zbog9CwuNDQULNRrTCLIn46/WmpR1f0w6PsCYQHkylZr5vkd6kfMZYV6pRnQkcPNRyiA8LsrNKyhpg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
+ "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
+ "dependencies": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.4",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "node_modules/engine.io/node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/engine.io/node_modules/ws": {
+ "version": "7.5.12",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.12.tgz",
+ "integrity": "sha512-1xGnbYN3zbog9CwuNDQULNRrTCLIn46/WmpR1f0w6PsCYQHkylZr5vkd6kfMZYV6pRnQkcPNRyiA8LsrNKyhpg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.19.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
+ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
+ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es6-promise": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-goat": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "node_modules/escape-quotes": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-quotes/-/escape-quotes-1.0.2.tgz",
+ "integrity": "sha512-JpLFzklNReeakCpyj59s78P5F72q0ZUpDnp2BuIk9TtTjj2HMsgiWBChw17BlZT8dRhMtmSb1jE2+pTP1iFYyw==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "node_modules/escape-quotes/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-compat-utils": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
+ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-formatter-pretty": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz",
+ "integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "^7.2.13",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.0",
+ "eslint-rule-docs": "^1.1.5",
+ "log-symbols": "^4.0.0",
+ "plur": "^4.0.0",
+ "string-width": "^4.2.0",
+ "supports-hyperlinks": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/@types/eslint": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
+ "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-formatter-pretty/node_modules/supports-hyperlinks": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
+ "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-chai-friendly": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-1.2.1.tgz",
+ "integrity": "sha512-mV3EOJLDr8+L+LS8uCkP711fnNHz+4PsmPyz18xwkvjJwfLRlnx0Eu6CFnb5B+dW5ahoav2jVer2KFYsmIuv3A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=3.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-cypress": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-6.4.3.tgz",
+ "integrity": "sha512-2/ulKNnCn+0vbaDI/6KovgGKsR/Y/bA+KMAkxRbHl2IAMd/UzU24ZOXpncR+QPBlaXqfAJ9NMCzdwX9Gjhm9AQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "globals": "^17.7.0"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": ">=8",
+ "eslint": ">=9"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/parser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-es-x": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
+ "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/ota-meshi",
+ "https://opencollective.com/eslint"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.1.2",
+ "@eslint-community/regexpp": "^4.11.0",
+ "eslint-compat-utils": "^0.5.1"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8"
+ }
+ },
+ "node_modules/eslint-plugin-icedfrisby": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-icedfrisby/-/eslint-plugin-icedfrisby-0.3.0.tgz",
+ "integrity": "sha512-VxnWNlvsfYN2eStzNAxJGUByL9Sggfdq5vFhzTdTPCXn+dNH/5KGmPZCRz4fTMCK2kAG7dEk6FC+vYR0OKnV8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "requireindex": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 22"
+ },
+ "peerDependencies": {
+ "eslint": ">= 9"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc": {
+ "version": "63.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.2.0.tgz",
+ "integrity": "sha512-tccz9igEV5mTKVAwo/KwXqKP7ENqa3a+LpRFuEPAP3k/+SXG4T0sOvA+c2wwTD/TLNrH9MCW4LIu4xEExkJdzg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@es-joy/jsdoccomment": "~0.88.0",
+ "@es-joy/resolve.exports": "1.2.0",
+ "are-docs-informative": "^0.0.2",
+ "comment-parser": "1.4.7",
+ "debug": "^4.4.3",
+ "escape-string-regexp": "^4.0.0",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
+ "html-entities": "^2.6.0",
+ "object-deep-merge": "^2.0.1",
+ "parse-imports-exports": "^0.2.4",
+ "semver": "^7.8.5",
+ "spdx-expression-parse": "^4.0.0",
+ "to-valid-identifier": "^1.0.0"
+ },
+ "engines": {
+ "node": "^22.13.0 || >=24"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
+ "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-mocha": {
+ "version": "11.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-11.3.0.tgz",
+ "integrity": "sha512-anENwrIwmdvunmmssjMn5a4nTd+mYMkqBlwjksxOECcIThLNhefWJIiTWY7pY/arMQFjNwHQjVOZb6pQ9PrLjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.1",
+ "globals": "^15.14.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-mocha/node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-n": {
+ "version": "17.24.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.24.0.tgz",
+ "integrity": "sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.5.0",
+ "enhanced-resolve": "^5.17.1",
+ "eslint-plugin-es-x": "^7.8.0",
+ "get-tsconfig": "^4.8.1",
+ "globals": "^15.11.0",
+ "globrex": "^0.1.2",
+ "ignore": "^5.3.2",
+ "semver": "^7.6.3",
+ "ts-declaration-location": "^1.0.6"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.23.0"
+ }
+ },
+ "node_modules/eslint-plugin-n/node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.5.6",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz",
+ "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.1",
+ "synckit": "^0.11.13"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-promise": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.3.0.tgz",
+ "integrity": "sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+ "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-sort-class-members": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-sort-class-members/-/eslint-plugin-sort-class-members-1.22.1.tgz",
+ "integrity": "sha512-TC76+m06fjpiAYPrEcyyvvWzp1aMHNBVGROLu9ijQuPDZPWJjhNTheha4f6f7eUTWabloczLZagoZ+mb6Cv0ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=0.8.0"
+ }
+ },
+ "node_modules/eslint-rule-docs": {
+ "version": "1.1.235",
+ "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz",
+ "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==",
+ "dev": true
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-scope/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-util-attach-comments": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz",
+ "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-build-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz",
+ "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-walker": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz",
+ "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "astring": "^1.8.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js/node_modules/source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/estree-util-value-to-estree": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.5.0.tgz",
+ "integrity": "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/remcohaszing"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eta": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz",
+ "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/eta-dev/eta?sponsor=1"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eval": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz",
+ "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "require-like": ">= 0.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/eventemitter2": {
+ "version": "6.4.7",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
+ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
+ "dev": true
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/executable": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
+ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/exponential-backoff": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
+ "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-patch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
+ "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
+ "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
+ "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.5.0",
+ "xml-naming": "^0.1.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.10.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz",
+ "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@nodable/entities": "^3.0.0",
+ "fast-xml-builder": "^1.2.0",
+ "is-unsafe": "^2.0.0",
+ "path-expression-matcher": "^1.6.2",
+ "strnum": "^2.4.1",
+ "xml-naming": "^0.3.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fast-xml-parser/node_modules/xml-naming": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz",
+ "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fault": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz",
+ "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "format": "^0.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/feed": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
+ "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-js": "^1.6.11"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/file-loader": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
+ "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/file-loader/node_modules/schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/file-type": {
+ "version": "21.3.4",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz",
+ "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tokenizer/inflate": "^0.4.1",
+ "strtok3": "^10.3.4",
+ "token-types": "^6.1.1",
+ "uint8array-extras": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/find-cache-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz",
+ "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "common-path-prefix": "^3.0.0",
+ "pkg-dir": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fleau": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/fleau/-/fleau-16.2.0.tgz",
+ "integrity": "sha512-Yr2MEL/jUzVW7vsMb6w5I+yKxuBj1vr6VSnZqNjQj/zsn5nyDOU0Sba7S1B+6wZSHW0+uWrMwG0XbE1x4K2dVw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/folktale": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.3.2.tgz",
+ "integrity": "sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ==",
+ "deprecated": "This package is no longer actively maintained. Only security patches will be provided, if needed. Consider switching to fp-ts.",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/foreach": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz",
+ "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/foreground-child/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/format": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
+ "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
+ "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
+ "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-func-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stdin": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
+ "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.7",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz",
+ "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/github-slugger": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz",
+ "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob-to-regex.js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz",
+ "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/glob/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-4.1.3.tgz",
+ "integrity": "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "globalthis": "^1.0.2",
+ "matcher": "^4.0.0",
+ "semver": "^7.3.5",
+ "serialize-error": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/global-agent/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/global-agent/node_modules/matcher": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz",
+ "integrity": "sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/global-dirs": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+ "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+ "dev": true,
+ "dependencies": {
+ "ini": "2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
+ "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-15.1.0.tgz",
+ "integrity": "sha512-DG+DAAkRtno+oDr/GBsliAkhN9+zczOPM5qXk3efDZY3qvyRnZU+NwQlb/IOY6cSnxxTR9z3aHCcShJyjb0hJA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^8.0.0",
+ "byte-counter": "^0.1.0",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^13.0.18",
+ "chunk-data": "^0.1.0",
+ "decompress-response": "^10.0.0",
+ "http2-wrapper": "^2.2.1",
+ "keyv": "^5.6.0",
+ "lowercase-keys": "^4.0.1",
+ "responselike": "^4.0.2",
+ "type-fest": "^5.6.0",
+ "uint8array-extras": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/got/node_modules/cacheable-request": {
+ "version": "13.0.19",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.19.tgz",
+ "integrity": "sha512-SVXGH037+Mo1aIMO5B2UcleR43FGjFdN+M8JObSyEoQ2Mn4CODRWx28gN5jiTF0n5ItsgtIZfyargMNs8GX4kg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-cache-semantics": "^4.2.0",
+ "get-stream": "^9.0.1",
+ "http-cache-semantics": "^4.2.0",
+ "keyv": "^5.6.0",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.1.1",
+ "responselike": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/got/node_modules/decompress-response": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-10.0.0.tgz",
+ "integrity": "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/keyv": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1"
+ }
+ },
+ "node_modules/got/node_modules/lowercase-keys": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-4.0.1.tgz",
+ "integrity": "sha512-wI9Nui/L8VfADa/cr/7NQruaASk1k23/Uh1khQ02BCVYiiy8F4AhOGnQzJy3Fl/c44GnYSbZHv8g7EcG3kJ1Qg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/responselike": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz",
+ "integrity": "sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==",
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/responselike/node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got/node_modules/type-fest": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz",
+ "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==",
+ "license": "(MIT OR CC0-1.0)",
+ "dependencies": {
+ "tagged-tag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/graphlib": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
+ "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "node_modules/graphql": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-17.0.2.tgz",
+ "integrity": "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "^22.0.0 || ^24.0.0 || ^25.0.0 || >=26.0.0"
+ }
+ },
+ "node_modules/graphql-tag": {
+ "version": "2.12.7",
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.7.tgz",
+ "integrity": "sha512-xnE/NFzy+0eIesvAsREJZ284zTl/wYuBAvpsFSDhRGRdRHdnE90M21Q3xAWyYInb0J756c6x0pIQ62+vtvOs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/gzip-size": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
+ "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/handle-thing": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg=="
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "deprecated": "this library is no longer supported",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/hard-rejection": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
+ "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dependencies": {
+ "isarray": "2.0.1"
+ }
+ },
+ "node_modules/has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-only": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/has-only/-/has-only-1.1.1.tgz",
+ "integrity": "sha512-3GuFy9rDw0xvovCHb4SOKiRImbZ+a8boFBUyGNRPVd2mRyQOzYdau5G9nodUXC1ZKYN59hrHFkW1lgBQscYfTg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-yarn": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz",
+ "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hasha": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
+ "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-stream": "^2.0.0",
+ "type-fest": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hasha/node_modules/type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5/node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5/node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+ "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector/node_modules/@types/hast": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+ "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2"
+ }
+ },
+ "node_modules/hast-util-parse-selector/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-estree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz",
+ "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-attach-comments": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-object": "^0.4.0",
+ "unist-util-position": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz",
+ "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-object": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz",
+ "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==",
+ "dev": true
+ },
+ "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz",
+ "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==",
+ "dev": true,
+ "dependencies": {
+ "inline-style-parser": "0.2.2"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5/node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+ "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^3.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript/node_modules/@types/hast": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+ "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2"
+ }
+ },
+ "node_modules/hastscript/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/history": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+ "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.1.2",
+ "loose-envify": "^1.2.0",
+ "resolve-pathname": "^3.0.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0",
+ "value-equal": "^1.0.1"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dev": true,
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "node_modules/hpack.js/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/hpack.js/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/hpack.js/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/hpack.js/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/html-entities": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
+ "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "node_modules/html-minifier-terser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+ "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "~5.3.2",
+ "commander": "^10.0.0",
+ "entities": "^4.4.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.15.1"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/html-minifier-terser/node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/html-tags": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz",
+ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/html-webpack-plugin": {
+ "version": "5.6.6",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz",
+ "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/html-minifier-terser": "^6.0.0",
+ "html-minifier-terser": "^6.0.2",
+ "lodash": "^4.17.21",
+ "pretty-error": "^4.0.0",
+ "tapable": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/html-webpack-plugin"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "webpack": "^5.20.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/html-webpack-plugin/node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "^5.2.2",
+ "commander": "^8.3.0",
+ "he": "^1.2.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.10.0"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-deceiver": {
+ "resolved": "vendor/http-deceiver",
+ "link": true
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-errors/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/http-proxy-middleware": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
+ "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-proxy": "^1.17.8",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.1",
+ "is-plain-obj": "^3.0.0",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "@types/express": "^4.17.13"
+ },
+ "peerDependenciesMeta": {
+ "@types/express": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/http-proxy-middleware/node_modules/is-plain-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/http-reasons": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz",
+ "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/http-signature": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz",
+ "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^2.0.2",
+ "sshpk": "^1.18.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/http2-client": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz",
+ "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http2-wrapper": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.12.0"
+ }
+ },
+ "node_modules/hyperdyperid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
+ "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.18"
+ }
+ },
+ "node_modules/hyperlinker": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz",
+ "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/icedfrisby": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/icedfrisby/-/icedfrisby-4.0.0.tgz",
+ "integrity": "sha512-LUjsxRoUl0Qb7dFTnov799183//txuVPxFY1YlESyhSatBmyYjsp0zxfrUmO3F78u+3XrOKROpVsXh6prpa4pw==",
+ "dev": true,
+ "dependencies": {
+ "chai": "^4.0.1",
+ "chai-subset": "^1.3.0",
+ "chai-things": "^0.2.0",
+ "chalk": "^4.1.0",
+ "check-types": "^11.1.2",
+ "lodash": "^4.16.6",
+ "qs": "^6.3.0",
+ "request": "^2.76.0",
+ "verror": "^1.10.0",
+ "wait-promise": "^0.4.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@hapi/joi": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "mocha": "*"
+ }
+ },
+ "node_modules/icedfrisby-nock": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/icedfrisby-nock/-/icedfrisby-nock-2.1.0.tgz",
+ "integrity": "sha512-kllF0451KHKkrwb5Em8KXA0i9ncAnezfidvgVi1sBuA8ClDorZ8A+rdr61dgcXZlo/MEWHeJIR5e0wVB/mxthA==",
+ "dev": true,
+ "peerDependencies": {
+ "icedfrisby": ">= 2.2.0",
+ "nock": "*"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/chai": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
+ "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
+ "dev": true,
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.3",
+ "deep-eql": "^4.1.3",
+ "get-func-name": "^2.0.2",
+ "loupe": "^2.3.6",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/check-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+ "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+ "dev": true,
+ "dependencies": {
+ "get-func-name": "^2.0.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/icedfrisby/node_modules/loupe": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
+ "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
+ "dev": true,
+ "dependencies": {
+ "get-func-name": "^2.0.1"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/icedfrisby/node_modules/type-detect": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
+ "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true
+ },
+ "node_modules/image-size": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
+ "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
+ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==",
+ "dev": true
+ },
+ "node_modules/immer": {
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-in-the-middle": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.0.tgz",
+ "integrity": "sha512-OnGy+eYT7wVejH2XWgLRgbmzujhhVIATQH0ztIeRilwHBjTeG3pD+XnH3PKX0r9gJ0BuJmJ68q/oh9qgXnNDQg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-import-attributes": "^1.9.5",
+ "cjs-module-lexer": "^2.2.0",
+ "module-details-from-path": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/import-lazy": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
+ "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+ },
+ "node_modules/infima": {
+ "version": "0.2.0-alpha.45",
+ "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz",
+ "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
+ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==",
+ "dev": true
+ },
+ "node_modules/install-artifact-from-github": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.7.0.tgz",
+ "integrity": "sha512-qAb91yAKVF9rFY4rVP21ZtYUyCScxAFt9udwzVWNLBE1pQcdQeB2gd1HlNPcQNYCzCDvJ/QJQPuWQ6aTmSlU8g==",
+ "license": "BSD-3-Clause",
+ "bin": {
+ "hash-github-cache": "bin/hash-github-cache.js",
+ "install-from-cache": "bin/install-from-cache.js",
+ "save-to-github-cache": "bin/save-to-github-cache.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/uhop"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/ip6": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/ip6/-/ip6-0.0.4.tgz",
+ "integrity": "sha512-0FJ0rKtTQYCp92ru/hNzNpmy2sa8nINqbuyiiVOI75+ltMtAly9dtEparm+xh//kndXL/s6FOdWzSYvUDiBpbg=="
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.2.0.tgz",
+ "integrity": "sha512-ku//LD7ie/m9UVGCm9KweBIIHP4mB0maNGvav6Hz778fQCNLQF7iZ+H/UuHuqmjJCHCpA5hw8hOeRKxZl8IlXw==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/irregular-plurals": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz",
+ "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "dev": true,
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-async-function": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
+ "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz",
+ "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-ci": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^3.2.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-in-ssh": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz",
+ "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-installed-globally": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+ "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+ "dev": true,
+ "dependencies": {
+ "global-dirs": "^3.0.0",
+ "is-path-inside": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-network-error": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz",
+ "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-npm": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz",
+ "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-reference": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
+ "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-svg": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-6.1.0.tgz",
+ "integrity": "sha512-i7YPdvYuSCYcaLQrKwt8cvKTlwHcdA6Hp8N9SO3Q5jIzo8x6kH3N47W0BvPP7NdxVBmIHx7X9DK36czYYW7lHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@file-type/xml": "^0.4.3"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unsafe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz",
+ "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
+ "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-yarn-global": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz",
+ "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
+ "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/iterate-object": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.4.tgz",
+ "integrity": "sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw=="
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/its-name": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/its-name/-/its-name-1.0.0.tgz",
+ "integrity": "sha512-GYUWFxViqxDvGzsNEItTEuOqqAQVx29Xl9Lh5YUqyJd6gPHTCMiIbjqcjjyUzsBUqEcgwIdRoydwgfFw1oYbhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jest-diff": {
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
+ "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.4.3",
+ "jest-get-type": "^29.4.3",
+ "pretty-format": "^29.5.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-diff/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-diff/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-diff/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/jest-diff/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/jest-diff/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "29.4.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
+ "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-util/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-util/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jest-util/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/jest-util/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jest-util/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
+ "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+ "dev": true,
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/joi": {
+ "version": "18.2.3",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
+ "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/address": "^5.1.1",
+ "@hapi/formula": "^3.0.2",
+ "@hapi/hoek": "^11.0.7",
+ "@hapi/pinpoint": "^2.0.1",
+ "@hapi/tlds": "^1.1.1",
+ "@hapi/topo": "^6.0.2",
+ "@standard-schema/spec": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/joi-extension-semver": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/joi-extension-semver/-/joi-extension-semver-5.0.0.tgz",
+ "integrity": "sha512-g23Majx+TFtxRHAcQn48j+GYts2E0opkUieNxJ6PzLz1dKKZHrKYuEt3p/JY+RgJmyEG961E0dekTuilns8fVA==",
+ "dependencies": {
+ "semver": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "joi": ">=17.2.0"
+ }
+ },
+ "node_modules/joi-extension-semver/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/joi/node_modules/@hapi/address": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
+ "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^11.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/joi/node_modules/@hapi/formula": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
+ "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/joi/node_modules/@hapi/hoek": {
+ "version": "11.0.7",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
+ "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/joi/node_modules/@hapi/topo": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
+ "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^11.0.2"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz",
+ "integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.mjs"
+ }
+ },
+ "node_modules/js2xmlparser": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
+ "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
+ "dev": true,
+ "dependencies": {
+ "xmlcreate": "^2.0.4"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "node_modules/jsdoc": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz",
+ "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/parser": "^7.20.15",
+ "@jsdoc/salty": "^0.2.1",
+ "@types/markdown-it": "^14.1.1",
+ "bluebird": "^3.7.2",
+ "catharsis": "^0.9.0",
+ "escape-string-regexp": "^2.0.0",
+ "js2xmlparser": "^4.0.2",
+ "klaw": "^3.0.0",
+ "markdown-it": "^14.1.0",
+ "markdown-it-anchor": "^8.6.7",
+ "marked": "^4.0.10",
+ "mkdirp": "^1.0.4",
+ "requizzle": "^0.2.3",
+ "strip-json-comments": "^3.1.0",
+ "underscore": "~1.13.2"
+ },
+ "bin": {
+ "jsdoc": "jsdoc.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/jsdoc-type-pratt-parser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz",
+ "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/jsdoc/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jsep": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
+ "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
+ "engines": {
+ "node": ">= 10.16.0"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/json-pointer": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz",
+ "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "foreach": "^2.0.4"
+ }
+ },
+ "node_modules/json-refs": {
+ "version": "3.0.15",
+ "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-3.0.15.tgz",
+ "integrity": "sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "~4.1.1",
+ "graphlib": "^2.1.8",
+ "js-yaml": "^3.13.1",
+ "lodash": "^4.17.15",
+ "native-promise-only": "^0.8.1",
+ "path-loader": "^1.0.10",
+ "slash": "^3.0.0",
+ "uri-js": "^4.2.2"
+ },
+ "bin": {
+ "json-refs": "bin/json-refs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/json-refs/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/json-refs/node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/json-refs/node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-refs/node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "node_modules/json-schema-compare": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz",
+ "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/json-schema-merge-allof": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz",
+ "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "compute-lcm": "^1.1.2",
+ "json-schema-compare": "^0.2.2",
+ "lodash": "^4.17.20"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/json-schema-resolve-allof": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/json-schema-resolve-allof/-/json-schema-resolve-allof-1.5.0.tgz",
+ "integrity": "sha512-Jgn6BQGSLDp3D7bTYrmCbP/p7SRFz5BfpeEJ9A7sXuVADMc14aaDN1a49zqk9D26wwJlcNvjRpT63cz1VgFZeg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "get-stdin": "^5.0.1",
+ "lodash": "^4.14.0"
+ },
+ "bin": {
+ "json-schema-resolve-allof": "bin/json-schema-resolve-allof"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonpath-plus": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.4.0.tgz",
+ "integrity": "sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jsep-plugin/assignment": "^1.3.0",
+ "@jsep-plugin/regex": "^1.0.4",
+ "jsep": "^1.4.0"
+ },
+ "bin": {
+ "jsonpath": "bin/jsonpath-cli.js",
+ "jsonpath-plus": "bin/jsonpath-cli.js"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/jsonpointer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz",
+ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
+ "dev": true,
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash": "^4.17.21",
+ "ms": "^2.1.1",
+ "semver": "^7.3.8"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsprim": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+ "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/jsprim/node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
+ "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.2",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/keygrip": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz",
+ "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+ "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/latest-version": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz",
+ "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "package-json": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/launch-editor": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz",
+ "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picocolors": "^1.1.1",
+ "shell-quote": "^1.8.4"
+ }
+ },
+ "node_modules/lazy-ass": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
+ "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
+ "dev": true,
+ "engines": {
+ "node": "> 0.8"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
+ "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/lint-staged": {
+ "version": "17.1.1",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.1.1.tgz",
+ "integrity": "sha512-FnHWpSe5cPRtrDG+soOuNdBxb4XQb2gN5EqpEWKdweyqyOfpl4QSjbrz3ilcIf0WXmkiNQGZZRQ23R5YtB3TEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^4.0.5",
+ "string-argv": "^0.3.2",
+ "tinyexec": "^1.2.4"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=22.22.1"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ },
+ "optionalDependencies": {
+ "yaml": "^2.9.0"
+ }
+ },
+ "node_modules/lint-staged/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/lint-staged/node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/liquid-json": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz",
+ "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
+ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^5.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/listr2/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dev": true,
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
+ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/loader-utils": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.at": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
+ "integrity": "sha512-GOTh0SEp+Yosnlpjic+8cl2WM9MykorogkGA9xyIFkkObQ3H3kNZqZ+ohuq4K3FrSVo7hMcZBMataJemrxC3BA==",
+ "dev": true
+ },
+ "node_modules/lodash.concat": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.concat/-/lodash.concat-4.5.0.tgz",
+ "integrity": "sha512-US6b1Nqek3shg2qmv1IjTN5P7tPL1RKu77VpdGtVprxmnTI/HlsHGqI2Oa5Irznk0ZB5IXHwocMeMZK8vf4+CA==",
+ "dev": true
+ },
+ "node_modules/lodash.countby": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.countby/-/lodash.countby-4.6.0.tgz",
+ "integrity": "sha512-RhdqSKPeVL9zWY5jSYHA2PHrV+lm2x/NfZd1uCUMEJXZoqFJ14MlSjnm+otBVdmlrkJ3trLKM07wuMszZ5GIbA=="
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "node_modules/lodash.filter": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+ "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==",
+ "dev": true
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "dev": true
+ },
+ "node_modules/lodash.isfunction": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+ "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
+ "dev": true
+ },
+ "node_modules/lodash.isobject": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
+ "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==",
+ "dev": true
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "dev": true
+ },
+ "node_modules/lodash.isundefined": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
+ "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==",
+ "dev": true
+ },
+ "node_modules/lodash.keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz",
+ "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==",
+ "dev": true
+ },
+ "node_modules/lodash.mapvalues": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
+ "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==",
+ "dev": true
+ },
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "dev": true
+ },
+ "node_modules/lodash.toarray": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+ "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
+ "dev": true
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+ "dev": true
+ },
+ "node_modules/lunr-languages": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.14.0.tgz",
+ "integrity": "sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA==",
+ "dev": true
+ },
+ "node_modules/map-obj": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
+ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/map-values": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz",
+ "integrity": "sha512-BbShUnr5OartXJe1GeccAWtfro11hhgNJg6G9/UtWKjVGvV5U4C09cg5nk8JUevhXODaXY+hQ3xxMUKSs62ONQ=="
+ },
+ "node_modules/mark.js": {
+ "version": "8.11.1",
+ "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz",
+ "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==",
+ "dev": true
+ },
+ "node_modules/markdown-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz",
+ "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "14.3.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz",
+ "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.5.0",
+ "linkify-it": "^5.0.2",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it-anchor": {
+ "version": "8.6.7",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
+ "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
+ "dev": true,
+ "peerDependencies": {
+ "@types/markdown-it": "*",
+ "markdown-it": "*"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/marked": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "dev": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-6.0.0.tgz",
+ "integrity": "sha512-TzDerdcNtI79w7Av4GT57bLdElPA/VAkjqdMZv8yhuc8geU2z0ljW9anXbX/55aHEMTpYypZb1lxsA/46r9oOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdast-util-directive": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz",
+ "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz",
+ "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/mdast-util-frontmatter": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz",
+ "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-extension-frontmatter": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
+ "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==",
+ "dev": true,
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz",
+ "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.0.tgz",
+ "integrity": "sha512-A8AJHlR7/wPQ3+Jre1+1rq040fX9A4Q1jG8JxmSNp/PLPHg80A6475wxTp3KzHpApFH6yWxFotHrJQA3dXP6/w==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-remove-position": "^5.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz",
+ "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.30",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+ "dev": true
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "dev": true
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/memfs": {
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.56.2.tgz",
+ "integrity": "sha512-AEbdVTy4TZiugbnfA7d1z9IvwpHlaGh9Vlb/iteHDtUU/WhOKAwgbhy1f8dnX1SMbeKLIXdXf3lVWb55PuBQQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jsonjoy.com/fs-core": "4.56.2",
+ "@jsonjoy.com/fs-fsa": "4.56.2",
+ "@jsonjoy.com/fs-node": "4.56.2",
+ "@jsonjoy.com/fs-node-builtins": "4.56.2",
+ "@jsonjoy.com/fs-node-to-fsa": "4.56.2",
+ "@jsonjoy.com/fs-node-utils": "4.56.2",
+ "@jsonjoy.com/fs-print": "4.56.2",
+ "@jsonjoy.com/fs-snapshot": "^4.56.2",
+ "@jsonjoy.com/json-pack": "^1.11.0",
+ "@jsonjoy.com/util": "^1.9.0",
+ "glob-to-regex.js": "^1.0.1",
+ "thingies": "^2.5.0",
+ "tree-dump": "^1.0.3",
+ "tslib": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
+ }
+ },
+ "node_modules/memfs-or-file-map-to-github-branch": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.3.0.tgz",
+ "integrity": "sha512-AzgIEodmt51dgwB3TmihTf1Fh2SmszdZskC6trFHy4v71R5shLmdjJSYI7ocVfFa7C/TE6ncb0OZ9eBg2rmkBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/rest": "*"
+ }
+ },
+ "node_modules/memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/meow": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz",
+ "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz",
+ "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz",
+ "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-extension-directive": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz",
+ "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-directive/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-frontmatter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz",
+ "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fault": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-extension-mdx-expression": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz",
+ "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz",
+ "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==",
+ "dev": true,
+ "dependencies": {
+ "@types/acorn": "^4.0.0",
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-extension-mdx-md": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz",
+ "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==",
+ "dev": true,
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz",
+ "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.0.0",
+ "acorn-jsx": "^5.0.0",
+ "micromark-extension-mdx-expression": "^3.0.0",
+ "micromark-extension-mdx-jsx": "^3.0.0",
+ "micromark-extension-mdx-md": "^2.0.0",
+ "micromark-extension-mdxjs-esm": "^3.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz",
+ "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz",
+ "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-destination/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz",
+ "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz",
+ "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space/node_modules/micromark-util-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz",
+ "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz",
+ "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz",
+ "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-character": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz",
+ "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "node_modules/micromark-util-character/node_modules/micromark-util-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz",
+ "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz",
+ "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz",
+ "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz",
+ "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz",
+ "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz",
+ "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz",
+ "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz",
+ "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "@types/acorn": "^4.0.0",
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz",
+ "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz",
+ "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz",
+ "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz",
+ "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz",
+ "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz",
+ "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz",
+ "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromark/node_modules/micromark-factory-space": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz",
+ "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark/node_modules/micromark-util-character": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz",
+ "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark/node_modules/micromark-util-symbol": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz",
+ "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ]
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-format": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz",
+ "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "charset": "^1.0.0"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mini-css-extract-plugin": {
+ "version": "2.10.2",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz",
+ "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "schema-utils": "^4.0.0",
+ "tapable": "^2.2.1"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minimist-options": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz",
+ "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
+ "dev": true,
+ "dependencies": {
+ "arrify": "^1.0.1",
+ "is-plain-obj": "^1.1.0",
+ "kind-of": "^6.0.3"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "11.7.6",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz",
+ "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^4.0.1",
+ "debug": "^4.3.5",
+ "diff": "^7.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^10.4.5",
+ "he": "^1.2.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^9.0.5",
+ "ms": "^2.1.3",
+ "picocolors": "^1.1.1",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^9.2.0",
+ "yargs": "^17.7.2",
+ "yargs-parser": "^21.1.1",
+ "yargs-unparser": "^2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/mocha/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mocha/node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/mocha/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mocha/node_modules/diff": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/mocha/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/mocha/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/mocha/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/mocha/node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/mocha/node_modules/workerpool": {
+ "version": "9.3.2",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz",
+ "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/mocha/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/module-details-from-path": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
+ "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
+ "license": "MIT"
+ },
+ "node_modules/monaco-editor": {
+ "version": "0.31.1",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz",
+ "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/multicast-dns": {
+ "version": "7.2.5",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
+ "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dns-packet": "^5.2.2",
+ "thunky": "^1.0.2"
+ },
+ "bin": {
+ "multicast-dns": "cli.js"
+ }
+ },
+ "node_modules/multilog": {
+ "version": "14.11.22",
+ "resolved": "https://registry.npmjs.org/multilog/-/multilog-14.11.22.tgz",
+ "integrity": "sha512-cStW7LsblpwzbaXYsTUGb3/+O2ztNwlXAfFpm+zDnC3+OHBgE51GliczWJXlSLUY0R/4ur2t3EujBvYMwTIV8w==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/mvncmp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mvncmp/-/mvncmp-1.0.0.tgz",
+ "integrity": "sha512-twFBVdKIWfSYhPeUl6gY2ccyFZyCqRATbnTuXfOMK3FQBMa90HupJvtWrQzOvIAC74BYL4anMYIVDF7zeONsuQ==",
+ "license": "Apache Licence Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)"
+ },
+ "node_modules/nan": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz",
+ "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/native-promise-only": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+ "integrity": "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "node_modules/neostandard": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/neostandard/-/neostandard-0.13.0.tgz",
+ "integrity": "sha512-R3iglFr+Dla/8qFBqsMxBvcYBOgP6rAGw7uRHKMpM3bUP0wLDRzUstxtEI9RfEwn7xszE/UUnh8H090Ru4Z52A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
+ "@stylistic/eslint-plugin": "2.11.0",
+ "eslint-plugin-n": "^17.23.2",
+ "eslint-plugin-promise": "^7.2.1",
+ "eslint-plugin-react": "^7.37.5",
+ "find-up": "^8.0.0",
+ "globals": "^17.3.0",
+ "peowly": "^1.3.3",
+ "typescript-eslint": "^8.56.0"
+ },
+ "bin": {
+ "neostandard": "cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "peerDependencies": {
+ "eslint": "^9.0.0"
+ }
+ },
+ "node_modules/neostandard/node_modules/find-up": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-8.0.0.tgz",
+ "integrity": "sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^8.0.0",
+ "unicorn-magic": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/neostandard/node_modules/locate-path": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-8.0.0.tgz",
+ "integrity": "sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/neostandard/node_modules/p-limit": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/neostandard/node_modules/p-locate": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+ "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/neostandard/node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/neotraverse": {
+ "version": "0.6.15",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.15.tgz",
+ "integrity": "sha512-HZpdkco+JeXq0G+WWpMJ4NsX3pqb5O7eR9uGz3FfoFt+LYzU8iRWp49nJtud6hsDoywM8tIrDo3gjgmOqJA8LA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/nock": {
+ "version": "13.5.6",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
+ "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.0",
+ "json-stringify-safe": "^5.0.1",
+ "propagate": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ }
+ },
+ "node_modules/node-cleanup": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
+ "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==",
+ "dev": true
+ },
+ "node_modules/node-emoji": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
+ "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.6.0",
+ "char-regex": "^1.0.2",
+ "emojilib": "^2.4.0",
+ "skin-tone": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/node-emoji/node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-fetch-h2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz",
+ "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "http2-client": "^1.2.5"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ }
+ },
+ "node_modules/node-gyp": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-13.0.1.tgz",
+ "integrity": "sha512-piOr0S10qy5THB+q5BdqkoOx65XL/tjTMUAit3vciPNp+snTOBnGunWH1Rz7XZUxf2T9uFrfT/Ty4+aC3yPeyg==",
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "nopt": "^10.0.0",
+ "proc-log": "^7.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.5.4",
+ "tinyglobby": "^0.2.12",
+ "undici": "^8.4.1",
+ "which": "^7.0.0"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ }
+ },
+ "node_modules/node-gyp/node_modules/abbrev": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-5.0.0.tgz",
+ "integrity": "sha512-/XrFJgzQQQHpti1raDJC6m4ws6aNktmjBlhk8Fdlk7LwCEuDoieEJJY9OFHjfiFJFFRM2tK+Ky/IsfbbmlMu1w==",
+ "license": "ISC",
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ }
+ },
+ "node_modules/node-gyp/node_modules/isexe": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
+ "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/node-gyp/node_modules/nopt": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-10.0.1.tgz",
+ "integrity": "sha512-df3sBr/6ax9hSGuC3CspvLlbnX8cP5L5nZwXF8cGN8l0zSWR6BvzmQ6jPUKjvo6+/xdpkNvEcucBNUdBeeV13g==",
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^5.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ }
+ },
+ "node_modules/node-gyp/node_modules/undici": {
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.8.0.tgz",
+ "integrity": "sha512-ubshXMXwF3MQIMF1y/WxZdNBnjEKeSg2wF5mcGUtU55YTw34tnVVpKRlLf7ruDXZ5344KokPVX4RBx1wJm64Bw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/node-gyp/node_modules/which": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-7.0.0.tgz",
+ "integrity": "sha512-RancgH2dmbLdHl6LRhEqvklWMgl/Hdnun0Y90KhBOLkMefg8Qa7/Zel8Sm+8HEcP6DEjzsWzpkuBQEZok58isA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^4.0.0"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ }
+ },
+ "node_modules/node-mocks-http": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.18.1.tgz",
+ "integrity": "sha512-hPMOLJZzhgT4i/zbYpfy1P2ulAlHtzFcrEGxPh/4pDRegCY3+p2sxDhQm1fxte5EgI/RwDx+NNW7v8kJcyxtMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^1.3.7",
+ "content-disposition": "^0.5.3",
+ "depd": "^1.1.0",
+ "fresh": "^0.5.2",
+ "merge-descriptors": "^1.0.1",
+ "methods": "^1.1.2",
+ "mime": "^1.3.4",
+ "range-parser": "^1.2.0",
+ "type-is": "^1.6.18"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "@types/express": "^4.17.21 || ^5.0.0",
+ "@types/node": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/express": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-pg-migrate": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-9.0.0.tgz",
+ "integrity": "sha512-lp5+UZx1KKgOz/y0h6BYGPuJ5wVlZTgiBPGXCGoX6Bs6X9LtiIhrI4V2yW4mPwnJBfptVq0KtLQtXHcoEKOyig==",
+ "license": "MIT",
+ "dependencies": {
+ "glob": "~13.0.0",
+ "jiti": "~2.7.0",
+ "yargs": "~18.0.0"
+ },
+ "bin": {
+ "node-pg-migrate": "bin/node-pg-migrate.js"
+ },
+ "engines": {
+ "node": ">=20.11.0"
+ },
+ "peerDependencies": {
+ "@types/pg": ">=6.0.0 <9.0.0",
+ "pg": ">=4.3.0 <9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/pg": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "license": "MIT"
+ },
+ "node_modules/node-pg-migrate/node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/node-readfiles": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz",
+ "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es6-promise": "^3.2.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.51",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
+ "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
+ "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^10.2.1",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/nodemon/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/nodemon/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/nodemon/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/nodemon/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz",
+ "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-all": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+ "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "memorystream": "^0.3.1",
+ "minimatch": "^3.0.4",
+ "pidtree": "^0.3.0",
+ "read-pkg": "^3.0.0",
+ "shell-quote": "^1.6.1",
+ "string.prototype.padend": "^3.0.0"
+ },
+ "bin": {
+ "npm-run-all": "bin/npm-run-all/index.js",
+ "run-p": "bin/run-p/index.js",
+ "run-s": "bin/run-s/index.js"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/cross-spawn": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
+ "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/pidtree": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+ "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
+ "dev": true,
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/null-loader": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz",
+ "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/null-loader/node_modules/schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/oas-kit-common": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz",
+ "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "fast-safe-stringify": "^2.0.7"
+ }
+ },
+ "node_modules/oas-linter": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz",
+ "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@exodus/schemasafe": "^1.0.0-rc.2",
+ "should": "^13.2.1",
+ "yaml": "^1.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/oas-resolver": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz",
+ "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "node-fetch-h2": "^2.3.0",
+ "oas-kit-common": "^1.0.8",
+ "reftools": "^1.1.9",
+ "yaml": "^1.10.0",
+ "yargs": "^17.0.1"
+ },
+ "bin": {
+ "resolve": "resolve.js"
+ },
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/oas-resolver-browser": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz",
+ "integrity": "sha512-Jw5elT/kwUJrnGaVuRWe1D7hmnYWB8rfDDjBnpQ+RYY/dzAewGXeTexXzt4fGEo6PUE4eqKqPWF79MZxxvMppA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "node-fetch-h2": "^2.3.0",
+ "oas-kit-common": "^1.0.8",
+ "path-browserify": "^1.0.1",
+ "reftools": "^1.1.9",
+ "yaml": "^1.10.0",
+ "yargs": "^17.0.1"
+ },
+ "bin": {
+ "resolve": "resolve.js"
+ },
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/oas-schema-walker": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz",
+ "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/oas-validator": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz",
+ "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "call-me-maybe": "^1.0.1",
+ "oas-kit-common": "^1.0.8",
+ "oas-linter": "^3.2.2",
+ "oas-resolver": "^2.5.6",
+ "oas-schema-walker": "^1.1.5",
+ "reftools": "^1.1.9",
+ "should": "^13.2.1",
+ "yaml": "^1.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-deep-merge": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz",
+ "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-filter": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
+ "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA=="
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "wsl-utils": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-cli": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-9.0.0.tgz",
+ "integrity": "sha512-4UHkLVm4tUM/ardg66uY3x1icgfCnunks5eFVFBzASO3b13Ow2Md3xs9YT7yXWFjXOBpauIeh/N9fvbziU1wkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "file-type": "^21.3.4",
+ "meow": "^14.1.0",
+ "open": "^11.0.0",
+ "tempy": "^3.2.0"
+ },
+ "bin": {
+ "open-cli": "cli.js"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-cli/node_modules/open": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz",
+ "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "default-browser": "^5.4.0",
+ "define-lazy-prop": "^3.0.0",
+ "is-in-ssh": "^1.0.0",
+ "is-inside-container": "^1.0.0",
+ "powershell-utils": "^0.1.0",
+ "wsl-utils": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open-cli/node_modules/wsl-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz",
+ "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-wsl": "^3.1.0",
+ "powershell-utils": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/openapi-to-postmanv2": {
+ "version": "4.25.0",
+ "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-4.25.0.tgz",
+ "integrity": "sha512-sIymbkQby0gzxt2Yez8YKB6hoISEel05XwGwNrAhr6+vxJWXNxkmssQc/8UEtVkuJ9ZfUXLkip9PYACIpfPDWg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "ajv": "8.11.0",
+ "ajv-draft-04": "1.0.0",
+ "ajv-formats": "2.1.1",
+ "async": "3.2.4",
+ "commander": "2.20.3",
+ "graphlib": "2.1.8",
+ "js-yaml": "4.1.0",
+ "json-pointer": "0.6.2",
+ "json-schema-merge-allof": "0.8.1",
+ "lodash": "4.17.21",
+ "neotraverse": "0.6.15",
+ "oas-resolver-browser": "2.5.6",
+ "object-hash": "3.0.0",
+ "path-browserify": "1.0.1",
+ "postman-collection": "^4.4.0",
+ "swagger2openapi": "7.0.8",
+ "yaml": "1.10.2"
+ },
+ "bin": {
+ "openapi2postmanv2": "bin/openapi2postmanv2.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/openapi-to-postmanv2/node_modules/ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/openapi-to-postmanv2/node_modules/ajv-draft-04": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
+ "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^8.5.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/openapi-to-postmanv2/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/openapi-to-postmanv2/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/openapi-to-postmanv2/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true,
+ "license": "(WTFPL OR MIT)",
+ "bin": {
+ "opener": "bin/opener-bin.js"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ospath": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
+ "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==",
+ "dev": true
+ },
+ "node_modules/override-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz",
+ "integrity": "sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==",
+ "dev": true
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
+ "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.4",
+ "p-timeout": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-retry": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
+ "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.2",
+ "is-network-error": "^1.0.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-retry/node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+ "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-finally": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-json": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz",
+ "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "got": "^12.1.0",
+ "registry-auth-token": "^5.0.1",
+ "registry-url": "^6.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/package-json/node_modules/@sindresorhus/is": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
+ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/package-json/node_modules/form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.17"
+ }
+ },
+ "node_modules/package-json/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json/node_modules/got": {
+ "version": "12.6.1",
+ "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
+ "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/package-json/node_modules/p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-diff": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.1.tgz",
+ "integrity": "sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==",
+ "dev": true
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
+ "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
+ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==",
+ "dev": true
+ },
+ "node_modules/parse-github-url": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz",
+ "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==",
+ "dev": true,
+ "bin": {
+ "parse-github-url": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-imports-exports": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz",
+ "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse-statements": "1.0.11"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-link-header": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz",
+ "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==",
+ "dependencies": {
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/parse-numeric-range": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz",
+ "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/parse-statements": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz",
+ "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dev": true,
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+ "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+ "dev": true,
+ "dependencies": {
+ "domhandler": "^5.0.2",
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-parser-stream": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
+ "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
+ "dev": true,
+ "dependencies": {
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parseqs": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+ "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
+ },
+ "node_modules/parseuri": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+ "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/path": {
+ "version": "0.12.7",
+ "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
+ "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "process": "^0.11.1",
+ "util": "^0.10.3"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
+ "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true,
+ "license": "(WTFPL OR MIT)"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-loader": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.12.tgz",
+ "integrity": "sha512-n7oDG8B+k/p818uweWrOixY9/Dsr89o2TkCm6tOTex3fpdo2+BFDgR+KpB37mGKBRsBAlR8CIJMFN0OEy/7hIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "native-promise-only": "^0.8.1",
+ "superagent": "^7.1.6"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz",
+ "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/peowly": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/peowly/-/peowly-1.3.3.tgz",
+ "integrity": "sha512-5UmUtvuCv3KzBX2NuQw2uF28o0t8Eq4KkPRZfUCzJs+DiNVKw7OaYn29vNDgrt/Pggs23CPlSTqgzlhHJfpT0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.6.0",
+ "typescript": ">=5.8"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "node_modules/periscopic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
+ "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "is-reference": "^3.0.0"
+ }
+ },
+ "node_modules/pg": {
+ "version": "8.22.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz",
+ "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.14.0",
+ "pg-pool": "^3.14.0",
+ "pg-protocol": "^1.15.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.4.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
+ "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz",
+ "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==",
+ "license": "MIT"
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz",
+ "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==",
+ "license": "MIT"
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinpoint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz",
+ "integrity": "sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg==",
+ "dev": true
+ },
+ "node_modules/pkg-dir": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz",
+ "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/find-up": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz",
+ "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^7.1.0",
+ "path-exists": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/locate-path": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+ "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^6.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-limit": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-locate": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+ "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkijs": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz",
+ "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@noble/hashes": "1.4.0",
+ "asn1js": "^3.0.6",
+ "bytestreamjs": "^2.0.1",
+ "pvtsutils": "^1.3.6",
+ "pvutils": "^1.1.3",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/plur": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+ "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
+ "dev": true,
+ "dependencies": {
+ "irregular-plurals": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/portfinder": {
+ "version": "1.0.38",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz",
+ "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "^3.2.6",
+ "debug": "^4.3.6"
+ },
+ "engines": {
+ "node": ">= 10.12"
+ }
+ },
+ "node_modules/portfinder/node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/portfinder/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/portfinder/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-attribute-case-insensitive": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz",
+ "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-calc": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
+ "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.2"
+ }
+ },
+ "node_modules/postcss-clamp": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz",
+ "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=7.6.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.6"
+ }
+ },
+ "node_modules/postcss-color-functional-notation": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz",
+ "integrity": "sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-hex-alpha": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz",
+ "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-rebeccapurple": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz",
+ "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-colormin": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
+ "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.3",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-convert-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
+ "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-custom-media": {
+ "version": "11.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz",
+ "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-properties": {
+ "version": "14.0.6",
+ "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz",
+ "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-selectors": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz",
+ "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/cascade-layer-name-parser": "^2.0.5",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-dir-pseudo-class": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz",
+ "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-discard-comments": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
+ "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-duplicates": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
+ "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-empty": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
+ "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-overridden": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
+ "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-unused": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz",
+ "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-double-position-gradients": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz",
+ "integrity": "sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-visible": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz",
+ "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-focus-within": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz",
+ "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-font-variant": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz",
+ "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-gap-properties": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz",
+ "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-image-set-function": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz",
+ "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/utilities": "^2.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-lab-function": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz",
+ "integrity": "sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/utilities": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-loader": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz",
+ "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cosmiconfig": "^8.3.5",
+ "jiti": "^1.20.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "postcss": "^7.0.0 || ^8.0.1",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/postcss-logical": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz",
+ "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-merge-idents": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz",
+ "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-merge-longhand": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz",
+ "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^6.1.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-merge-rules": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz",
+ "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-font-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz",
+ "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-gradients": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
+ "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "colord": "^2.9.3",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-params": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
+ "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz",
+ "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+ "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+ "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+ "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
"dev": true,
- "requires": {
- "chalk": "^2.4.2"
- },
+ "license": "ISC",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-nesting": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz",
+ "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-resolve-nested": "^3.1.0",
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "log-update": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz",
- "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=",
+ "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz",
+ "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==",
"dev": true,
- "requires": {
- "ansi-escapes": "^3.0.0",
- "cli-cursor": "^2.0.0",
- "wrap-ansi": "^3.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
},
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- },
- "wrap-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
- "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=",
- "dev": true,
- "requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0"
- }
- }
- }
- },
- "loglevel": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz",
- "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==",
- "dev": true
- },
- "lokijs": {
- "version": "1.5.8",
- "resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.8.tgz",
- "integrity": "sha512-D8E3TBrY35o1ELnonp2MF8b3wKu2tVNl2TqRjvS+95oPMMe7OoIAxNY1qr+5BEZwnWn2V4ErAjVt000DonM+FA==",
- "dev": true
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ }
},
- "lolex": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz",
- "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==",
+ "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
"dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
}
},
- "loose-envify": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
- "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "node_modules/postcss-nesting/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
"dev": true,
- "requires": {
- "js-tokens": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "loud-rejection": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "node_modules/postcss-normalize-charset": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
+ "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
"dev": true,
- "requires": {
- "currently-unhandled": "^0.4.1",
- "signal-exit": "^3.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
- "dev": true
- },
- "lru-cache": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
- "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+ "node_modules/postcss-normalize-display-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
+ "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
"dev": true,
- "requires": {
- "pseudomap": "^1.0.2",
- "yallist": "^2.1.2"
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "lru_map": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
- "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0="
- },
- "macos-release": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
- "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==",
- "dev": true
- },
- "make-dir": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
- "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "node_modules/postcss-normalize-positions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
+ "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
"dev": true,
- "requires": {
- "pify": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
},
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-repeat-style": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
+ "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- }
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true
+ "node_modules/postcss-normalize-string": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
+ "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
},
- "mamacro": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
- "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
- "dev": true
+ "node_modules/postcss-normalize-timing-functions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
+ "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
},
- "map-age-cleaner": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
- "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "node_modules/postcss-normalize-unicode": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
+ "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
"dev": true,
- "requires": {
- "p-defer": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
},
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-url": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
+ "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "p-defer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
- "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
- "dev": true
- }
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "map-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
- "dev": true
+ "node_modules/postcss-normalize-whitespace": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
+ "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
},
- "map-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz",
- "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=",
- "dev": true
+ "node_modules/postcss-opacity-percentage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz",
+ "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "kofi",
+ "url": "https://ko-fi.com/mrcgrtz"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mrcgrtz"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "map-stream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
- "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
- "dev": true
+ "node_modules/postcss-ordered-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
+ "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
},
- "map-values": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz",
- "integrity": "sha1-douOecAJvytk/ugG4ip7HEGQyZA="
+ "node_modules/postcss-overflow-shorthand": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz",
+ "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "node_modules/postcss-page-break": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz",
+ "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==",
"dev": true,
- "requires": {
- "object-visit": "^1.0.0"
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8"
}
},
- "markdown-it": {
+ "node_modules/postcss-place": {
"version": "10.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
- "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz",
+ "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-preset-env": {
+ "version": "10.6.1",
+ "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.6.1.tgz",
+ "integrity": "sha512-yrk74d9EvY+W7+lO9Aj1QmjWY9q5NsKjK2V9drkOPZB/X6KZ0B3igKsHUYakb7oYVhnioWypQX3xGuePf89f3g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/postcss-alpha-function": "^1.0.1",
+ "@csstools/postcss-cascade-layers": "^5.0.2",
+ "@csstools/postcss-color-function": "^4.0.12",
+ "@csstools/postcss-color-function-display-p3-linear": "^1.0.1",
+ "@csstools/postcss-color-mix-function": "^3.0.12",
+ "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.2",
+ "@csstools/postcss-content-alt-text": "^2.0.8",
+ "@csstools/postcss-contrast-color-function": "^2.0.12",
+ "@csstools/postcss-exponential-functions": "^2.0.9",
+ "@csstools/postcss-font-format-keywords": "^4.0.0",
+ "@csstools/postcss-gamut-mapping": "^2.0.11",
+ "@csstools/postcss-gradients-interpolation-method": "^5.0.12",
+ "@csstools/postcss-hwb-function": "^4.0.12",
+ "@csstools/postcss-ic-unit": "^4.0.4",
+ "@csstools/postcss-initial": "^2.0.1",
+ "@csstools/postcss-is-pseudo-class": "^5.0.3",
+ "@csstools/postcss-light-dark-function": "^2.0.11",
+ "@csstools/postcss-logical-float-and-clear": "^3.0.0",
+ "@csstools/postcss-logical-overflow": "^2.0.0",
+ "@csstools/postcss-logical-overscroll-behavior": "^2.0.0",
+ "@csstools/postcss-logical-resize": "^3.0.0",
+ "@csstools/postcss-logical-viewport-units": "^3.0.4",
+ "@csstools/postcss-media-minmax": "^2.0.9",
+ "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5",
+ "@csstools/postcss-nested-calc": "^4.0.0",
+ "@csstools/postcss-normalize-display-values": "^4.0.1",
+ "@csstools/postcss-oklab-function": "^4.0.12",
+ "@csstools/postcss-position-area-property": "^1.0.0",
+ "@csstools/postcss-progressive-custom-properties": "^4.2.1",
+ "@csstools/postcss-property-rule-prelude-list": "^1.0.0",
+ "@csstools/postcss-random-function": "^2.0.1",
+ "@csstools/postcss-relative-color-syntax": "^3.0.12",
+ "@csstools/postcss-scope-pseudo-class": "^4.0.1",
+ "@csstools/postcss-sign-functions": "^1.1.4",
+ "@csstools/postcss-stepped-value-functions": "^4.0.9",
+ "@csstools/postcss-syntax-descriptor-syntax-production": "^1.0.1",
+ "@csstools/postcss-system-ui-font-family": "^1.0.0",
+ "@csstools/postcss-text-decoration-shorthand": "^4.0.3",
+ "@csstools/postcss-trigonometric-functions": "^4.0.9",
+ "@csstools/postcss-unset-value": "^4.0.0",
+ "autoprefixer": "^10.4.23",
+ "browserslist": "^4.28.1",
+ "css-blank-pseudo": "^7.0.1",
+ "css-has-pseudo": "^7.0.3",
+ "css-prefers-color-scheme": "^10.0.0",
+ "cssdb": "^8.6.0",
+ "postcss-attribute-case-insensitive": "^7.0.1",
+ "postcss-clamp": "^4.1.0",
+ "postcss-color-functional-notation": "^7.0.12",
+ "postcss-color-hex-alpha": "^10.0.0",
+ "postcss-color-rebeccapurple": "^10.0.0",
+ "postcss-custom-media": "^11.0.6",
+ "postcss-custom-properties": "^14.0.6",
+ "postcss-custom-selectors": "^8.0.5",
+ "postcss-dir-pseudo-class": "^9.0.1",
+ "postcss-double-position-gradients": "^6.0.4",
+ "postcss-focus-visible": "^10.0.1",
+ "postcss-focus-within": "^9.0.1",
+ "postcss-font-variant": "^5.0.0",
+ "postcss-gap-properties": "^6.0.0",
+ "postcss-image-set-function": "^7.0.0",
+ "postcss-lab-function": "^7.0.12",
+ "postcss-logical": "^8.1.0",
+ "postcss-nesting": "^13.0.2",
+ "postcss-opacity-percentage": "^3.0.0",
+ "postcss-overflow-shorthand": "^6.0.0",
+ "postcss-page-break": "^3.0.4",
+ "postcss-place": "^10.0.0",
+ "postcss-pseudo-class-any-link": "^10.0.1",
+ "postcss-replace-overflow-wrap": "^4.0.0",
+ "postcss-selector-not": "^8.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-pseudo-class-any-link": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz",
+ "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==",
"dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "entities": "~2.0.0",
- "linkify-it": "^2.0.0",
- "mdurl": "^1.0.1",
- "uc.micro": "^1.0.5"
- },
- "dependencies": {
- "entities": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
- "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
- "dev": true
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
}
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
}
},
- "markdown-it-anchor": {
- "version": "5.2.7",
- "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.7.tgz",
- "integrity": "sha512-REFmIaSS6szaD1bye80DMbp7ePwsPNvLTR5HunsUcZ0SG0rWJQ+Pz24R4UlTKtjKBPhxo0v0tOBDYjZQQknW8Q==",
- "dev": true
- },
- "marked": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
- "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==",
- "dev": true
- },
- "md5": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
- "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+ "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
"dev": true,
- "requires": {
- "charenc": "~0.0.1",
- "crypt": "~0.0.1",
- "is-buffer": "~1.1.1"
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "md5-file": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz",
- "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==",
+ "node_modules/postcss-reduce-idents": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz",
+ "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==",
"dev": true,
- "requires": {
- "buffer-alloc": "^1.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "md5.js": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
- "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "node_modules/postcss-reduce-initial": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
+ "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
"dev": true,
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0"
},
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-reduce-transforms": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
+ "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
- "dev": true
- }
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "mdn-data": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
- "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==",
- "dev": true
+ "node_modules/postcss-replace-overflow-wrap": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz",
+ "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.0.3"
+ }
},
- "mdurl": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
- "dev": true
+ "node_modules/postcss-selector-not": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz",
+ "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
},
- "meant": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.1.tgz",
- "integrity": "sha512-UakVLFjKkbbUwNWJ2frVLnnAtbb7D7DsloxRd3s/gDpI8rdv8W5Hp3NaDb+POBI1fQdeussER6NB8vpcRURvlg==",
- "dev": true
+ "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+ "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
- "dev": true
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
+ "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "mem": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
- "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+ "node_modules/postcss-sort-media-queries": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz",
+ "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==",
"dev": true,
- "requires": {
- "map-age-cleaner": "^0.1.1",
- "mimic-fn": "^2.0.0",
- "p-is-promise": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "sort-css-media-queries": "2.2.0"
},
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.23"
+ }
+ },
+ "node_modules/postcss-svgo": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
+ "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- }
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^3.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >= 18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "memfs-or-file-map-to-github-branch": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.0.tgz",
- "integrity": "sha512-PloI9AkRXrLQuBU1s7eYQpl+4hkL0U0h23lddMaJ3ZGUufn8pdNRxd1kCfBqL5gISCFQs78ttXS15e4/f5vcTA==",
+ "node_modules/postcss-unique-selectors": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz",
+ "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
"dev": true,
- "requires": {
- "@octokit/rest": "^16.43.1"
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "memoize-one": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
- "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==",
- "dev": true
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
},
- "memory-fs": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
- "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "node_modules/postcss-zindex": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz",
+ "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==",
"dev": true,
- "requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "memorystream": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
- "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
- "dev": true
- },
- "meow": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz",
- "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==",
- "dev": true,
- "requires": {
- "camelcase-keys": "^4.0.0",
- "decamelize-keys": "^1.0.0",
- "loud-rejection": "^1.0.0",
- "minimist-options": "^3.0.1",
- "normalize-package-data": "^2.3.4",
- "read-pkg-up": "^3.0.0",
- "redent": "^2.0.0",
- "trim-newlines": "^2.0.0",
- "yargs-parser": "^10.0.0"
- },
- "dependencies": {
- "camelcase": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
- "dev": true
- },
- "load-json-file": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
- "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^4.0.0",
- "pify": "^3.0.0",
- "strip-bom": "^3.0.0"
- }
- },
- "parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
- "dev": true,
- "requires": {
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1"
- }
- },
- "path-type": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
- "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
- "dev": true,
- "requires": {
- "pify": "^3.0.0"
- }
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "read-pkg": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
- "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
- "dev": true,
- "requires": {
- "load-json-file": "^4.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^3.0.0"
- }
- },
- "read-pkg-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
- "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
- "dev": true,
- "requires": {
- "find-up": "^2.0.0",
- "read-pkg": "^3.0.0"
- }
- },
- "yargs-parser": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
- "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
- "dev": true,
- "requires": {
- "camelcase": "^4.1.0"
- }
- }
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
}
},
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
- "dev": true
+ "node_modules/postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "merge2": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz",
- "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==",
- "dev": true
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
- "dev": true
+ "node_modules/postman-code-generators": {
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/postman-code-generators/-/postman-code-generators-1.14.2.tgz",
+ "integrity": "sha512-qZAyyowfQAFE4MSCu2KtMGGQE/+oG1JhMZMJNMdZHYCSfQiVVeKxgk3oI4+KJ3d1y5rrm2D6C6x+Z+7iyqm+fA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "3.2.2",
+ "detect-package-manager": "3.0.2",
+ "lodash": "4.17.21",
+ "path": "0.12.7",
+ "postman-collection": "^4.4.0",
+ "shelljs": "0.8.5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
- "microevent.ts": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
- "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==",
- "dev": true
+ "node_modules/postman-code-generators/node_modules/async": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz",
+ "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==",
+ "dev": true,
+ "license": "MIT"
},
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
- },
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- },
- "kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true
- }
+ "node_modules/postman-collection": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.5.0.tgz",
+ "integrity": "sha512-152JSW9pdbaoJihwjc7Q8lc3nPg/PC9lPTHdMk7SHnHhu/GBJB7b2yb9zG7Qua578+3PxkQ/HYBuXpDSvsf7GQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@faker-js/faker": "5.5.3",
+ "file-type": "3.9.0",
+ "http-reasons": "0.1.0",
+ "iconv-lite": "0.6.3",
+ "liquid-json": "0.3.1",
+ "lodash": "4.17.21",
+ "mime-format": "2.0.1",
+ "mime-types": "2.1.35",
+ "postman-url-encoder": "3.0.5",
+ "semver": "7.6.3",
+ "uuid": "8.3.2"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "miller-rabin": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
- "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "node_modules/postman-collection/node_modules/file-type": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+ "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
"dev": true,
- "requires": {
- "bn.js": "^4.0.0",
- "brorand": "^1.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "dev": true
- },
- "mime-db": {
- "version": "1.30.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
- "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
- },
- "mime-types": {
- "version": "2.1.17",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
- "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
- "requires": {
- "mime-db": "~1.30.0"
+ "node_modules/postman-collection/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "mimic-fn": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
- "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
- "dev": true
- },
- "mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
- "dev": true
+ "node_modules/postman-url-encoder": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz",
+ "integrity": "sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
},
- "min-document": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
- "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+ "node_modules/powershell-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz",
+ "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==",
"dev": true,
- "requires": {
- "dom-walk": "^0.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "mini-css-extract-plugin": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz",
- "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==",
- "dev": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "normalize-url": "1.9.1",
- "schema-utils": "^1.0.0",
- "webpack-sources": "^1.1.0"
- },
- "dependencies": {
- "normalize-url": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
- "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
- "dev": true,
- "requires": {
- "object-assign": "^4.0.1",
- "prepend-http": "^1.0.0",
- "query-string": "^4.1.0",
- "sort-keys": "^1.0.0"
- }
- },
- "prepend-http": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
- "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
- "dev": true
- },
- "query-string": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
- "dev": true,
- "requires": {
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
- }
- },
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- },
- "sort-keys": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
- "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
- "dev": true,
- "requires": {
- "is-plain-obj": "^1.0.0"
- }
- },
- "strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
- "dev": true
- }
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ "node_modules/prettier": {
+ "version": "3.9.6",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz",
+ "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
},
- "minimalistic-crypto-utils": {
+ "node_modules/prettier-linter-helpers": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz",
+ "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
},
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "requires": {
- "brace-expansion": "^1.1.7"
+ "node_modules/pretty-bytes": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-7.1.0.tgz",
+ "integrity": "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ "node_modules/pretty-error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+ "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.20",
+ "renderkid": "^3.0.0"
+ }
},
- "minimist-options": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz",
- "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==",
+ "node_modules/pretty-format": {
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+ "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
- "requires": {
- "arrify": "^1.0.1",
- "is-plain-obj": "^1.1.0"
+ "dependencies": {
+ "@jest/schemas": "^29.4.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "mississippi": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
- "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
- "requires": {
- "concat-stream": "^1.5.0",
- "duplexify": "^3.4.2",
- "end-of-stream": "^1.1.0",
- "flush-write-stream": "^1.0.0",
- "from2": "^2.1.0",
- "parallel-transform": "^1.1.0",
- "pump": "^3.0.0",
- "pumpify": "^1.3.3",
- "stream-each": "^1.1.0",
- "through2": "^2.0.0"
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "mitt": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz",
- "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==",
+ "node_modules/pretty-format/node_modules/react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
- "mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "node_modules/pretty-time": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz",
+ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==",
"dev": true,
- "requires": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
}
},
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "node_modules/prettyjson": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz",
+ "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==",
"dev": true,
- "requires": {
- "minimist": "0.0.8"
+ "dependencies": {
+ "colors": "1.4.0",
+ "minimist": "^1.2.0"
},
+ "bin": {
+ "prettyjson": "bin/prettyjson"
+ }
+ },
+ "node_modules/priorityqueuejs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-2.0.0.tgz",
+ "integrity": "sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ=="
+ },
+ "node_modules/prism-react-renderer": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz",
+ "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==",
+ "dev": true,
"dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- }
+ "@types/prismjs": "^1.26.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0"
}
},
- "mocha": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz",
- "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==",
- "dev": true,
- "requires": {
- "ansi-colors": "3.2.3",
- "browser-stdout": "1.3.1",
- "chokidar": "3.3.0",
- "debug": "3.2.6",
- "diff": "3.5.0",
- "escape-string-regexp": "1.0.5",
- "find-up": "3.0.0",
- "glob": "7.1.3",
- "growl": "1.10.5",
- "he": "1.2.0",
- "js-yaml": "3.13.1",
- "log-symbols": "3.0.0",
- "minimatch": "3.0.4",
- "mkdirp": "0.5.5",
- "ms": "2.1.1",
- "node-environment-flags": "1.0.6",
- "object.assign": "4.1.0",
- "strip-json-comments": "2.0.1",
- "supports-color": "6.0.0",
- "which": "1.3.1",
- "wide-align": "1.1.3",
- "yargs": "13.3.2",
- "yargs-parser": "13.1.2",
- "yargs-unparser": "1.6.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "cliui": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
- "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
- "dev": true,
- "requires": {
- "string-width": "^3.1.0",
- "strip-ansi": "^5.2.0",
- "wrap-ansi": "^5.1.0"
- }
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "mkdirp": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
- "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
- },
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
- "dev": true
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "supports-color": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
- "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "wrap-ansi": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
- "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "string-width": "^3.0.0",
- "strip-ansi": "^5.0.0"
- }
- },
- "yargs": {
- "version": "13.3.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
- "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
- "dev": true,
- "requires": {
- "cliui": "^5.0.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^3.0.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^13.1.2"
- }
- },
- "yargs-parser": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
- "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
}
},
- "mocha-bamboo-reporter": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/mocha-bamboo-reporter/-/mocha-bamboo-reporter-1.1.2.tgz",
- "integrity": "sha512-q5yq/5rro0mXgfcmIDbEa+iYuOzrLTk+pK0t8ma321yFt1aWIRi24Q1wanVWRKh2meLfBXp1UfigJ25QyAT4Iw==",
- "dev": true
+ "node_modules/proc-log": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-7.0.0.tgz",
+ "integrity": "sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==",
+ "license": "ISC",
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ }
},
- "mocha-env-reporter": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mocha-env-reporter/-/mocha-env-reporter-4.0.0.tgz",
- "integrity": "sha512-QX5uKjYq+7CRFu1IxfEiV7YNfluuisi1CWYTgbONjM16t2EVDa649o3bmmV9yEbGAHiEQkQQp8ZY4RiYwVxJDQ==",
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"dev": true,
- "requires": {
- "mocha-bamboo-reporter": "*",
- "mocha-jenkins-reporter": "*",
- "mocha-teamcity-reporter": "*"
+ "engines": {
+ "node": ">= 0.6.0"
}
},
- "mocha-jenkins-reporter": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/mocha-jenkins-reporter/-/mocha-jenkins-reporter-0.4.1.tgz",
- "integrity": "sha512-IqnIylrkKJG0lxeoawRkhv/uiYojMEw3o9TQOpDFarPYKVq4ymngVPwsyfMB0XMDqtDbOTOCviFg8xOLHb80/Q==",
- "dev": true,
- "requires": {
- "diff": "1.0.7",
- "mkdirp": "0.5.1",
- "mocha": "^5.2.0",
- "xml": "^1.0.1"
- },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "node_modules/prom-client": {
+ "version": "15.1.3",
+ "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz",
+ "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==",
"dependencies": {
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
- "dev": true
- },
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "diff": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz",
- "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=",
- "dev": true
- },
- "glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "he": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
- "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
- "dev": true
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "mocha": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
- "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
- "dev": true,
- "requires": {
- "browser-stdout": "1.3.1",
- "commander": "2.15.1",
- "debug": "3.1.0",
- "diff": "3.5.0",
- "escape-string-regexp": "1.0.5",
- "glob": "7.1.2",
- "growl": "1.10.5",
- "he": "1.1.1",
- "minimatch": "3.0.4",
- "mkdirp": "0.5.1",
- "supports-color": "5.4.0"
- },
- "dependencies": {
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
- }
- }
- }
- },
- "mocha-junit-reporter": {
- "version": "1.23.3",
- "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz",
- "integrity": "sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==",
- "dev": true,
- "requires": {
- "debug": "^2.2.0",
- "md5": "^2.1.0",
- "mkdirp": "~0.5.1",
- "strip-ansi": "^4.0.0",
- "xml": "^1.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
+ "@opentelemetry/api": "^1.4.0",
+ "tdigest": "^0.1.1"
+ },
+ "engines": {
+ "node": "^16 || ^18 || >=20"
}
},
- "mocha-teamcity-reporter": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/mocha-teamcity-reporter/-/mocha-teamcity-reporter-2.5.2.tgz",
- "integrity": "sha512-zaJHKye3DSui3e9FdRKyt7fj9JxQxvlmSUu1Jow0VTyiYu6VsETY57Q4gwb2PWPZZgAugluamSnMCYC17hutGg==",
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
"dev": true,
- "requires": {
- "mocha": ">=3.5.0 < 6"
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
},
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
"dependencies": {
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
- "dev": true
- },
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "he": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
- "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
- "dev": true
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mocha": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
- "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
- "dev": true,
- "requires": {
- "browser-stdout": "1.3.1",
- "commander": "2.15.1",
- "debug": "3.1.0",
- "diff": "3.5.0",
- "escape-string-regexp": "1.0.5",
- "glob": "7.1.2",
- "growl": "1.10.5",
- "he": "1.1.1",
- "minimatch": "3.0.4",
- "mkdirp": "0.5.1",
- "supports-color": "5.4.0"
- },
- "dependencies": {
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- }
- }
- }
- }
- },
- "mocha-yaml-loader": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/mocha-yaml-loader/-/mocha-yaml-loader-1.0.3.tgz",
- "integrity": "sha1-3oI7r1GGrGbz/ZrIEpAeNkPMoWQ=",
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/propagate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
+ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
"dev": true,
- "requires": {
- "js-yaml": "^3.8.2"
+ "engines": {
+ "node": ">= 8"
}
},
- "moment": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ "node_modules/property-information": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz",
+ "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
},
- "moo": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
- "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==",
- "dev": true
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true,
+ "license": "ISC"
},
- "move-concurrently": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
- "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dev": true,
- "requires": {
- "aproba": "^1.1.1",
- "copy-concurrently": "^1.0.0",
- "fs-write-stream-atomic": "^1.0.8",
- "mkdirp": "^0.5.1",
- "rimraf": "^2.5.4",
- "run-queue": "^1.0.3"
- },
+ "license": "MIT",
"dependencies": {
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
}
},
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
- "multicast-dns": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
- "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+ "node_modules/proxy-addr/node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true,
- "requires": {
- "dns-packet": "^1.3.1",
- "thunky": "^1.0.2"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
}
},
- "multicast-dns-service-types": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
- "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+ "node_modules/proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==",
"dev": true
},
- "multilog": {
- "version": "14.11.22",
- "resolved": "https://registry.npmjs.org/multilog/-/multilog-14.11.22.tgz",
- "integrity": "sha1-w/AQjQnR/bNBDq8eGT5SkI4jpDM="
- },
- "mute-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
- "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
- "name-all-modules-plugin": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz",
- "integrity": "sha1-Cr+2rYNXGLn7Te8GdOBmV6lUN1w=",
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
- "nan": {
- "version": "2.14.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
- "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
- "optional": true
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
},
- "nanomatch": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz",
- "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-odd": "^2.0.0",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pupa": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz",
+ "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-goat": "^4.0.0"
},
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pvtsutils": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
+ "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
- "dev": true
- },
- "array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
- "dev": true
- },
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/pvutils": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
+ "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
},
- "kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
- "dev": true
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
+ ]
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "node_modules/quote": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/quote/-/quote-0.4.0.tgz",
+ "integrity": "sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==",
"dev": true
},
- "nearley": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.0.tgz",
- "integrity": "sha512-2v52FTw7RPqieZr3Gth1luAXZR7Je6q3KaDHY5bjl/paDUdMu35fZ8ICNgiYJRr3tf3NMvIQQR1r27AvEr9CRA==",
+ "node_modules/r-json": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.2.10.tgz",
+ "integrity": "sha512-hu9vyLjSlHXT62NAS7DjI9WazDlvjN0lgp3n431dCVnirVcLkZIpzSwA3orhZEKzdDD2jqNYI+w0yG0aFf4kpA=="
+ },
+ "node_modules/ramda": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
+ "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true,
- "requires": {
- "commander": "^2.19.0",
- "moo": "^0.4.3",
- "railroad-diagrams": "^1.0.0",
- "randexp": "0.4.6",
- "semver": "^5.4.1"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ramda"
}
},
- "negotiator": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
- "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
},
- "neo-async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
- "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
- "dev": true
+ "node_modules/range_check": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/range_check/-/range_check-1.4.0.tgz",
+ "integrity": "sha512-UhRtGxKoAm7NQqAPj3P6JVUlgs3hehoV+pA593il2FiHYZUK49/535laoi3Rvz7lP04XLCAdgbL4TprpVZZ/IQ==",
+ "dependencies": {
+ "ip6": "0.0.4",
+ "ipaddr.js": "1.2"
+ }
},
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
},
- "nise": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/nise/-/nise-3.0.1.tgz",
- "integrity": "sha512-fYcH9y0drBGSoi88kvhpbZEsenX58Yr+wOJ4/Mi1K4cy+iGP/a73gNoyNhu5E9QxPdgTlVChfIaAlnyOy/gHUA==",
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0",
- "@sinonjs/formatio": "^4.0.1",
- "@sinonjs/text-encoding": "^0.7.1",
- "just-extend": "^4.0.2",
- "lolex": "^5.0.1",
- "path-to-regexp": "^1.7.0"
- },
+ "license": "MIT",
"dependencies": {
- "path-to-regexp": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
- "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
- "dev": true,
- "requires": {
- "isarray": "0.0.1"
- }
- }
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
}
},
- "nock": {
- "version": "11.9.1",
- "resolved": "https://registry.npmjs.org/nock/-/nock-11.9.1.tgz",
- "integrity": "sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA==",
+ "node_modules/raw-body/node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
- "requires": {
- "debug": "^4.1.0",
- "json-stringify-safe": "^5.0.1",
- "lodash": "^4.17.13",
- "mkdirp": "^0.5.0",
- "propagate": "^2.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/raw-body/node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
},
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
}
},
- "node-cleanup": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
- "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=",
- "dev": true
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
},
- "node-emoji": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
- "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"dev": true,
- "requires": {
- "lodash.toarray": "^4.4.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "node-env-flag": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/node-env-flag/-/node-env-flag-0.1.0.tgz",
- "integrity": "sha1-vn1DxRHCeBqg+GiOfY9Cb3WSS8U=",
- "requires": {
- "chai": "^4.2.0"
- },
- "dependencies": {
- "chai": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
- "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
- "requires": {
- "assertion-error": "^1.1.0",
- "check-error": "^1.0.2",
- "deep-eql": "^3.0.1",
- "get-func-name": "^2.0.0",
- "pathval": "^1.1.0",
- "type-detect": "^4.0.5"
- }
- },
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
- }
+ "node_modules/re2": {
+ "version": "1.26.1",
+ "resolved": "https://registry.npmjs.org/re2/-/re2-1.26.1.tgz",
+ "integrity": "sha512-oi79a4h6EO3PAwNsDMWgeCcsRGQEUa52DIgOiFTZGDEZocEXG9h+oXy0qZqndo47huUeJuVWSoOJIEhOupqOcg==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "install-artifact-from-github": "^1.7.0",
+ "nan": "^2.28.0",
+ "node-gyp": "^13.0.1"
+ },
+ "engines": {
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/uhop"
}
},
- "node-environment-flags": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
- "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+ "node_modules/react": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
+ "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
"dev": true,
- "requires": {
- "object.getownpropertydescriptors": "^2.0.3",
- "semver": "^5.7.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "node-eta": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/node-eta/-/node-eta-0.9.0.tgz",
- "integrity": "sha1-n7CwmbzSoCGUDmA8ZCVNwAPZp6g=",
- "dev": true
- },
- "node-fetch": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
- "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
- "dev": true
+ "node_modules/react-dom": {
+ "version": "19.2.8",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
+ "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.8"
+ }
},
- "node-forge": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
- "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
- "dev": true
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "dev": true,
+ "license": "MIT"
},
- "node-libs-browser": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
- "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
- "dev": true,
- "requires": {
- "assert": "^1.1.1",
- "browserify-zlib": "^0.2.0",
- "buffer": "^4.3.0",
- "console-browserify": "^1.1.0",
- "constants-browserify": "^1.0.0",
- "crypto-browserify": "^3.11.0",
- "domain-browser": "^1.1.1",
- "events": "^3.0.0",
- "https-browserify": "^1.0.0",
- "os-browserify": "^0.3.0",
- "path-browserify": "0.0.1",
- "process": "^0.11.10",
- "punycode": "^1.2.4",
- "querystring-es3": "^0.2.0",
- "readable-stream": "^2.3.3",
- "stream-browserify": "^2.0.1",
- "stream-http": "^2.7.2",
- "string_decoder": "^1.0.0",
- "timers-browserify": "^2.0.4",
- "tty-browserify": "0.0.0",
- "url": "^0.11.0",
- "util": "^0.11.0",
- "vm-browserify": "^1.0.1"
- }
- },
- "node-mocks-http": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.8.1.tgz",
- "integrity": "sha512-qtd9YwXzCTdLfqjP7XSOtFei3TggwnjFIppmYEneQBaDIuknwgJTpItLskC5/pWOpU3lsK5aqdo+5CfIKHkXLg==",
+ "node_modules/react-helmet-async": {
+ "name": "@slorber/react-helmet-async",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
+ "integrity": "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==",
"dev": true,
- "requires": {
- "accepts": "^1.3.7",
- "depd": "^1.1.0",
- "fresh": "^0.5.2",
- "merge-descriptors": "^1.0.1",
- "methods": "^1.1.2",
- "mime": "^1.3.4",
- "parseurl": "^1.3.3",
- "range-parser": "^1.2.0",
- "type-is": "^1.6.18"
- },
+ "license": "Apache-2.0",
"dependencies": {
- "accepts": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
- "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
- "dev": true,
- "requires": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.2"
- }
- },
- "mime-db": {
- "version": "1.42.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
- "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.25",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
- "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
- "dev": true,
- "requires": {
- "mime-db": "1.42.0"
- }
- },
- "negotiator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
- "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
- "dev": true
- }
+ "@babel/runtime": "^7.12.5",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.2.0",
+ "shallowequal": "^1.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "node-modules-regexp": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
- "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
- "dev": true
- },
- "node-object-hash": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/node-object-hash/-/node-object-hash-2.0.0.tgz",
- "integrity": "sha512-VZR0zroAusy1ETZMZiGeLkdu50LGjG5U1KHZqTruqtTyQ2wfWhHG2Ow4nsUbfTFGlaREgNHcCWoM/OzEm6p+NQ==",
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
- "node-preload": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
- "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
+ "node_modules/react-json-view-lite": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.5.0.tgz",
+ "integrity": "sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g==",
"dev": true,
- "requires": {
- "process-on-spawn": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0"
}
},
- "node-releases": {
- "version": "1.1.47",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.47.tgz",
- "integrity": "sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA==",
+ "node_modules/react-loadable": {
+ "name": "@docusaurus/react-loadable",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz",
+ "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==",
"dev": true,
- "requires": {
- "semver": "^6.3.0"
- },
"dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "@types/react": "*"
+ },
+ "peerDependencies": {
+ "react": "*"
}
},
- "node-status-codes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-3.0.0.tgz",
- "integrity": "sha512-sLrGak9EYPczM9zVeQ9chTOa75KPi6EQN3KRaxsNjcJ9bFpYqM6abhTP67/JTOpgUM2kGZFChTU1YrPyAq6cIw=="
- },
- "node-version": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.1.0.tgz",
- "integrity": "sha512-t1V2RFiaTavaW3jtQO0A2nok6k7/Gghuvx2rjvICuT0B0dYaObBQ4U0xHL+ZTPFZodt1LMYG2Vi2nypfz4/AJg==",
- "dev": true
- },
- "nodemon": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.3.tgz",
- "integrity": "sha512-lLQLPS90Lqwc99IHe0U94rDgvjo+G9I4uEIxRG3evSLROcqQ9hwc0AxlSHKS4T1JW/IMj/7N5mthiN58NL/5kw==",
+ "node_modules/react-loadable-ssr-addon-v5-slorber": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.3.tgz",
+ "integrity": "sha512-GXfh9VLwB5ERaCsU6RULh7tkemeX15aNh6wuMEBtfdyMa7fFG8TXrhXlx1SoEK2Ty/l6XIkzzYIQmyaWW3JgdQ==",
"dev": true,
- "requires": {
- "chokidar": "^3.2.2",
- "debug": "^3.2.6",
- "ignore-by-default": "^1.0.1",
- "minimatch": "^3.0.4",
- "pstree.remy": "^1.1.7",
- "semver": "^5.7.1",
- "supports-color": "^5.5.0",
- "touch": "^3.1.0",
- "undefsafe": "^2.0.2",
- "update-notifier": "^4.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
+ "@babel/runtime": "^7.10.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "peerDependencies": {
+ "react-loadable": "*",
+ "webpack": ">=4.41.1 || 5.x"
}
},
- "noms": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
- "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=",
+ "node_modules/react-magic-dropzone": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/react-magic-dropzone/-/react-magic-dropzone-1.0.1.tgz",
+ "integrity": "sha512-0BIROPARmXHpk4AS3eWBOsewxoM5ndk2psYP/JmbCq8tz3uR2LIV1XiroZ9PKrmDRMctpW+TvsBCtWasuS8vFA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "readable-stream": "~1.0.31"
- },
- "dependencies": {
- "readable-stream": {
- "version": "1.0.34",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
- "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
- "dev": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.1",
- "isarray": "0.0.1",
- "string_decoder": "~0.10.x"
- }
+ "license": "MIT"
+ },
+ "node_modules/react-redux": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
+ "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.6",
+ "use-sync-external-store": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25 || ^19",
+ "react": "^18.0 || ^19",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
},
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
- "dev": true
+ "redux": {
+ "optional": true
}
}
},
- "nopt": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
- "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "node_modules/react-router": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
+ "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==",
"dev": true,
- "requires": {
- "abbrev": "1"
+ "dependencies": {
+ "@babel/runtime": "^7.12.13",
+ "history": "^4.9.0",
+ "hoist-non-react-statics": "^3.1.0",
+ "loose-envify": "^1.3.1",
+ "path-to-regexp": "^1.7.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.6.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=15"
}
},
- "normalize-package-data": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
- "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "node_modules/react-router-config": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz",
+ "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==",
"dev": true,
- "requires": {
- "hosted-git-info": "^2.1.4",
- "is-builtin-module": "^1.0.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "semver": {
- "version": "5.7.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
- "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
- "dev": true
- }
+ "@babel/runtime": "^7.1.2"
+ },
+ "peerDependencies": {
+ "react": ">=15",
+ "react-router": ">=5"
}
},
- "normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "node_modules/react-router-dom": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz",
+ "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==",
"dev": true,
- "requires": {
- "remove-trailing-separator": "^1.0.1"
+ "dependencies": {
+ "@babel/runtime": "^7.12.13",
+ "history": "^4.9.0",
+ "loose-envify": "^1.3.1",
+ "prop-types": "^15.6.2",
+ "react-router": "5.3.4",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=15"
}
},
- "normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
- "dev": true
- },
- "normalize-url": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
- "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
+ "node_modules/react-router/node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true
},
- "npm-run-all": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
- "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
+ "node_modules/react-router/node_modules/path-to-regexp": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz",
+ "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==",
"dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "chalk": "^2.4.1",
- "cross-spawn": "^6.0.5",
- "memorystream": "^0.3.1",
- "minimatch": "^3.0.4",
- "pidtree": "^0.3.0",
- "read-pkg": "^3.0.0",
- "shell-quote": "^1.6.1",
- "string.prototype.padend": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
- "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
- "dev": true
- }
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "load-json-file": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
- "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^4.0.0",
- "pify": "^3.0.0",
- "strip-bom": "^3.0.0"
- }
- },
- "parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
- "dev": true,
- "requires": {
- "error-ex": "^1.3.1",
- "json-parse-better-errors": "^1.0.1"
- }
- },
- "path-type": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
- "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
- "dev": true,
- "requires": {
- "pify": "^3.0.0"
- }
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "read-pkg": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
- "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
- "dev": true,
- "requires": {
- "load-json-file": "^4.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^3.0.0"
- }
- }
- }
- },
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
- "dev": true,
- "requires": {
- "path-key": "^2.0.0"
+ "isarray": "0.0.1"
}
},
- "nth-check": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
- "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
+ "node_modules/read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
"dev": true,
- "requires": {
- "boolbase": "~1.0.0"
+ "dependencies": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "null-loader": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-0.1.1.tgz",
- "integrity": "sha1-F76av80/8OFRL2/Er8sfUDk3j64=",
- "dev": true
- },
- "num2fraction": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
- "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
- "dev": true
- },
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "node_modules/read-pkg/node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
- "nyc": {
- "version": "15.0.1",
- "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.1.tgz",
- "integrity": "sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg==",
+ "node_modules/read-pkg/node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
- "requires": {
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "caching-transform": "^4.0.0",
- "convert-source-map": "^1.7.0",
- "decamelize": "^1.2.0",
- "find-cache-dir": "^3.2.0",
- "find-up": "^4.1.0",
- "foreground-child": "^2.0.0",
- "glob": "^7.1.6",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-hook": "^3.0.0",
- "istanbul-lib-instrument": "^4.0.0",
- "istanbul-lib-processinfo": "^2.0.2",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.2",
- "make-dir": "^3.0.0",
- "node-preload": "^0.2.1",
- "p-map": "^3.0.0",
- "process-on-spawn": "^1.0.0",
- "resolve-from": "^5.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "spawn-wrap": "^2.0.0",
- "test-exclude": "^6.0.0",
- "yargs": "^15.0.2"
- },
"dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "find-cache-dir": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
- "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.2",
- "pkg-dir": "^4.1.0"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
- "dev": true,
- "requires": {
- "aggregate-error": "^3.0.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "requires": {
- "find-up": "^4.0.0"
- }
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- },
- "yargs": {
- "version": "15.3.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
- "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
- "dev": true,
- "requires": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.1"
- }
- },
- "yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
}
},
- "oauth-sign": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
- },
- "object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
- },
- "object-component": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
- "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
- },
- "object-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
- "dev": true,
- "requires": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "dev": true
- }
- }
- }
- }
- },
- "object-filter": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
- "integrity": "sha1-rwt5f/6+r4pSxmN87b6IFs/sG8g="
- },
- "object-hash": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
- "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
- "dev": true
- },
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
- "dev": true
+ "node_modules/read-pkg/node_modules/path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "object-is": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
- "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=",
- "dev": true
+ "node_modules/read-pkg/node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
},
- "object-keys": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
- "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
+ "node_modules/read-pkg/node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
},
- "object-path": {
- "version": "0.11.4",
- "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz",
- "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=",
- "dev": true
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
},
- "object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
- "requires": {
- "isobject": "^3.0.0"
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
}
},
- "object.assign": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
- "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "node_modules/readline-sync": {
+ "version": "1.4.10",
+ "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
+ "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "function-bind": "^1.1.1",
- "has-symbols": "^1.0.0",
- "object-keys": "^1.0.11"
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "object.entries": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
- "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
+ "node_modules/rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.12.0",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
- "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.0",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "is-callable": "^1.1.4",
- "is-regex": "^1.0.4",
- "object-keys": "^1.0.12"
- }
- },
- "es-to-primitive": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
- "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "is-callable": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
- "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
- "dev": true
- },
- "is-symbol": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
- "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.0"
- }
- },
- "object-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
- "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
- "dev": true
- }
+ "dependencies": {
+ "resolve": "^1.1.6"
+ },
+ "engines": {
+ "node": ">= 0.10"
}
},
- "object.entries-ponyfill": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object.entries-ponyfill/-/object.entries-ponyfill-1.0.1.tgz",
- "integrity": "sha1-Kavfd8v70mVm3RqiTp2I9lQz0lY=",
- "dev": true
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
+ "dev": true,
+ "license": "MIT"
},
- "object.fromentries": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz",
- "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==",
+ "node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
- }
+ "license": "Apache-2.0"
},
- "object.getownpropertydescriptors": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
- "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz",
+ "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "es-abstract": "^1.5.1"
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "dunder-proto": "^1.0.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "node_modules/refractor": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz",
+ "integrity": "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==",
"dev": true,
- "requires": {
- "isobject": "^3.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
+ "@types/hast": "^2.0.0",
+ "@types/prismjs": "^1.0.0",
+ "hastscript": "^7.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "object.values": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz",
- "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=",
+ "node_modules/refractor/node_modules/@types/hast": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+ "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "es-abstract": "^1.6.1",
- "function-bind": "^1.1.0",
- "has": "^1.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2"
}
},
- "obuf": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
- "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
+ "node_modules/refractor/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "dev": true,
+ "license": "MIT"
},
- "octokit-pagination-methods": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz",
- "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==",
+ "node_modules/reftools": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz",
+ "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
"dev": true
},
- "on-finished": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
- "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
+ "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
"dev": true,
- "requires": {
- "ee-first": "1.1.1"
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"dev": true
},
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "requires": {
- "wrappy": "1"
+ "node_modules/regenerator-transform": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
+ "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.8.4"
}
},
- "onetime": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
- "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "node_modules/regex-escape": {
+ "version": "3.4.10",
+ "resolved": "https://registry.npmjs.org/regex-escape/-/regex-escape-3.4.10.tgz",
+ "integrity": "sha512-qEqf7uzW+iYcKNLMDFnMkghhQBnGdivT6KqVQyKsyjSWnoFyooXVnxrw9dtv3AFLnD6VBGXxtZGAQNFGFTnCqA=="
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
- "requires": {
- "mimic-fn": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "open": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/open/-/open-6.3.0.tgz",
- "integrity": "sha512-6AHdrJxPvAXIowO/aIaeHZ8CeMdDf7qCyRNq8NwJpinmCdXhz+NZR7ie1Too94lpciCDsG+qHGO9Mt0svA4OqA==",
+ "node_modules/regexpu-core": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
+ "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
"dev": true,
- "requires": {
- "is-wsl": "^1.1.0"
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.0",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.12.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "opencollective-postinstall": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
- "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==",
- "dev": true
+ "node_modules/registry-auth-token": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz",
+ "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pnpm/npm-conf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rc": "1.2.8"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "opentracing": {
- "version": "0.14.4",
- "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.4.tgz",
- "integrity": "sha512-nNnZDkUNExBwEpb7LZaeMeQgvrlO8l4bgY/LvGNZCR0xG/dGWqHqjKrAmR5GUoYo0FIz38kxasvA1aevxWs2CA==",
+ "node_modules/regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
"dev": true
},
- "opn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz",
- "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==",
+ "node_modules/regjsparser": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
+ "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
"dev": true,
- "requires": {
- "is-wsl": "^1.1.0"
+ "dependencies": {
+ "jsesc": "~3.0.2"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
}
},
- "opn-cli": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/opn-cli/-/opn-cli-5.0.0.tgz",
- "integrity": "sha512-+KGOnL1M9C9BbgLh6KvbSshAmOZqJtblq5w/1tpNRswCJchn9kmD4ZgQLj6mVMr7DzLpE8M/kRrvGoIYYca4sg==",
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
+ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
"dev": true,
- "requires": {
- "file-type": "^11.0.0",
- "get-stdin": "^7.0.0",
- "meow": "^5.0.0",
- "open": "^6.3.0",
- "temp-write": "^4.0.0"
+ "bin": {
+ "jsesc": "bin/jsesc"
},
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "get-stdin": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz",
- "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==",
- "dev": true
- }
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "optimize-css-assets-webpack-plugin": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
- "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==",
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
"dev": true,
- "requires": {
- "cssnano": "^4.1.10",
- "last-call-webpack-plugin": "^3.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
}
},
- "optionator": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
- "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
- "requires": {
- "deep-is": "~0.1.3",
- "fast-levenshtein": "~2.0.4",
- "levn": "~0.3.0",
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2",
- "wordwrap": "~1.0.0"
- }
- },
- "original": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
- "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+ "node_modules/remark-directive": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz",
+ "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==",
"dev": true,
- "requires": {
- "url-parse": "^1.4.3"
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-directive": "^3.0.0",
+ "micromark-extension-directive": "^3.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "os-browserify": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
- "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
- "dev": true
+ "node_modules/remark-emoji": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz",
+ "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.2",
+ "emoticon": "^4.0.1",
+ "mdast-util-find-and-replace": "^3.0.1",
+ "node-emoji": "^2.1.0",
+ "unified": "^11.0.4"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
},
- "os-locale": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
- "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "node_modules/remark-frontmatter": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz",
+ "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
"dev": true,
- "requires": {
- "execa": "^1.0.0",
- "lcid": "^2.0.0",
- "mem": "^4.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-frontmatter": "^2.0.0",
+ "micromark-extension-frontmatter": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "os-name": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
- "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==",
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
"dev": true,
- "requires": {
- "macos-release": "^2.2.0",
- "windows-release": "^3.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
- "dev": true
+ "node_modules/remark-mdx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz",
+ "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==",
+ "dev": true,
+ "dependencies": {
+ "mdast-util-mdx": "^3.0.0",
+ "micromark-extension-mdxjs": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "ospath": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
- "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=",
- "dev": true
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "override-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz",
- "integrity": "sha1-auIvresfhQ/7DPTCD/e4fl62UN8=",
- "dev": true
+ "node_modules/remark-rehype": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz",
+ "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "p-cancelable": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.0.0.tgz",
- "integrity": "sha512-USgPoaC6tkTGlS831CxsVdmZmyb8tR1D+hStI84MyckLOzfJlYQUweomrwE3D8T7u5u5GVuW064LT501wHTYYA==",
- "dev": true
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "p-defer": {
+ "node_modules/renderkid": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz",
- "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+ "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "css-select": "^4.1.3",
+ "dom-converter": "^0.2.0",
+ "htmlparser2": "^6.1.0",
+ "lodash": "^4.17.21",
+ "strip-ansi": "^6.0.1"
+ }
},
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
- "dev": true
+ "node_modules/renderkid/node_modules/css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
},
- "p-is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
- "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
- "dev": true
+ "node_modules/renderkid/node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
},
- "p-limit": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
- "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=",
- "dev": true
+ "node_modules/renderkid/node_modules/domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
},
- "p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "node_modules/renderkid/node_modules/domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dev": true,
- "requires": {
- "p-limit": "^1.1.0"
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
}
},
- "p-map": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
- "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
- "dev": true
+ "node_modules/renderkid/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
},
- "p-retry": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
- "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
+ "node_modules/renderkid/node_modules/htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"dev": true,
- "requires": {
- "retry": "^0.12.0"
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
}
},
- "p-timeout": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
- "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
+ "node_modules/request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"dev": true,
- "requires": {
- "p-finally": "^1.0.0"
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "p-try": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
- "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
- "dev": true
+ "node_modules/request-progress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+ "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==",
+ "dev": true,
+ "dependencies": {
+ "throttleit": "^1.0.0"
+ }
+ },
+ "node_modules/request/node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
},
- "package-hash": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
- "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
+ "node_modules/request/node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.15",
- "hasha": "^5.0.0",
- "lodash.flattendeep": "^4.4.0",
- "release-zalgo": "^1.0.0"
- },
"dependencies": {
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
- "dev": true
- }
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
}
},
- "package-json": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
- "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
+ "node_modules/request/node_modules/jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dev": true,
- "requires": {
- "got": "^9.6.0",
- "registry-auth-token": "^4.0.0",
- "registry-url": "^5.0.0",
- "semver": "^6.2.0"
- },
"dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
}
},
- "pako": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
- "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
- "dev": true
- },
- "parallel-transform": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
- "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+ "node_modules/request/node_modules/qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"dev": true,
- "requires": {
- "cyclist": "^1.0.1",
- "inherits": "^2.0.3",
- "readable-stream": "^2.1.5"
+ "engines": {
+ "node": ">=0.6"
}
},
- "parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "node_modules/request/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
- "requires": {
- "callsites": "^3.0.0"
+ "bin": {
+ "uuid": "bin/uuid"
}
},
- "parse-asn1": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
- "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
+ "node_modules/request/node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"dev": true,
- "requires": {
- "asn1.js": "^4.0.0",
- "browserify-aes": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.0",
- "pbkdf2": "^3.0.3",
- "safe-buffer": "^5.1.1"
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
}
},
- "parse-diff": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.0.tgz",
- "integrity": "sha512-88ar4IU2Fx+KgiyvVul0Y+YprMArTgLscO0nEKaEvWvhzRSSYefdf1M9PUmpx1pu5crUoVl2QNDMqjxfCeKptQ==",
- "dev": true
- },
- "parse-git-config": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-2.0.3.tgz",
- "integrity": "sha512-Js7ueMZOVSZ3tP8C7E3KZiHv6QQl7lnJ+OkbxoaFazzSa2KyEHqApfGbU3XboUgUnq4ZuUmskUpYKTNx01fm5A==",
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
- "requires": {
- "expand-tilde": "^2.0.2",
- "git-config-path": "^1.0.1",
- "ini": "^1.3.5"
- },
- "dependencies": {
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
- "dev": true
- }
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "parse-github-url": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz",
- "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==",
- "dev": true
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "node_modules/require-like": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz",
+ "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==",
"dev": true,
- "requires": {
- "error-ex": "^1.2.0"
+ "engines": {
+ "node": "*"
}
},
- "parse-link-header": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-1.0.1.tgz",
- "integrity": "sha1-vt/g0hGK64S+deewJUGeyKYRQKc=",
- "requires": {
- "xtend": "~4.0.1"
+ "node_modules/requireindex": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
+ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.5"
}
},
- "parse-passwd": {
+ "node_modules/requires-port": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
- "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
- "dev": true
- },
- "parse-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.1.tgz",
- "integrity": "sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA==",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true,
- "requires": {
- "is-ssh": "^1.3.0",
- "protocols": "^1.4.0"
- }
+ "license": "MIT"
},
- "parse-url": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz",
- "integrity": "sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg==",
+ "node_modules/requizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
+ "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
"dev": true,
- "requires": {
- "is-ssh": "^1.3.0",
- "normalize-url": "^3.3.0",
- "parse-path": "^4.0.0",
- "protocols": "^1.4.0"
+ "dependencies": {
+ "lodash": "^4.17.21"
}
},
- "parse5": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
- "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"dev": true,
- "requires": {
- "@types/node": "*"
- }
+ "license": "MIT"
},
- "parseqs": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
- "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
- "requires": {
- "better-assert": "~1.0.0"
+ "node_modules/reserved-identifiers": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz",
+ "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "parseuri": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
- "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
- "requires": {
- "better-assert": "~1.0.0"
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "dev": true
- },
- "pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
- "dev": true
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
},
- "password-prompt": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.2.tgz",
- "integrity": "sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^3.1.0",
- "cross-spawn": "^6.0.5"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
}
},
- "path-browserify": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
- "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
- "dev": true
- },
- "path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
- "dev": true
- },
- "path-exists": {
+ "node_modules/resolve-pathname": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
- "dev": true
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
- },
- "path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
- "dev": true
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
- "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
+ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
"dev": true
},
- "path-to-regexp": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-5.0.0.tgz",
- "integrity": "sha512-VOQVA+0mivusQfaveeWlrW7ddXKuStHMTdovn/1epf7cEpUL4dpzGl7eq8ho96H9w90/q66yJLsqn84jSCfkmQ=="
- },
- "path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
- "dev": true,
- "requires": {
- "pify": "^2.0.0"
- }
- },
- "pathval": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
- "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
- },
- "pause-stream": {
- "version": "0.0.11",
- "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
- "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
- "requires": {
- "through": "~2.3"
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
- "pbkdf2": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
- "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "node_modules/responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
"dev": true,
- "requires": {
- "create-hash": "^1.1.2",
- "create-hmac": "^1.1.4",
- "ripemd160": "^2.0.1",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
- "dev": true
- },
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
- },
- "physical-cpu-count": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz",
- "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=",
- "dev": true
- },
- "picomatch": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz",
- "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==",
- "dev": true
- },
- "pidtree": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
- "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
- "dev": true
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- },
- "pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
- "dev": true
- },
- "pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true,
- "requires": {
- "pinkie": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "pinpoint": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz",
- "integrity": "sha1-DPd1eml38b9/ajIge3CeN3OI6HQ=",
- "dev": true
- },
- "pirates": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
- "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true,
- "requires": {
- "node-modules-regexp": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "pkg-dir": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
- "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
- "requires": {
- "find-up": "^2.1.0"
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
- "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true,
- "requires": {
- "find-up": "^2.1.0"
+ "engines": {
+ "node": ">= 4"
}
},
- "please-upgrade-node": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
- "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
- "requires": {
- "semver-compare": "^1.0.0"
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
}
},
- "pluralize": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
- "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true
},
- "pnp-webpack-plugin": {
- "version": "1.6.4",
- "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
- "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==",
+ "node_modules/rimraf": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz",
+ "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==",
"dev": true,
- "requires": {
- "ts-pnp": "^1.1.6"
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "glob": "^13.0.3",
+ "package-json-from-dist": "^1.0.1"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "popmotion": {
- "version": "8.7.1",
- "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-8.7.1.tgz",
- "integrity": "sha512-XVQQnlmc8nLKBbR9gGVtsjNe/g1mOzg9TzWR2cE2bztvZe/IaSDDV6OY8HZcbcL+yUg3lJn4hh4MWbV+J8mj9Q==",
+ "node_modules/rtlcss": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz",
+ "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==",
"dev": true,
- "requires": {
- "@popmotion/easing": "^1.0.1",
- "@popmotion/popcorn": "^0.4.4",
- "framesync": "^4.0.0",
- "hey-listen": "^1.0.5",
- "style-value-types": "^3.1.7",
- "stylefire": "^7.0.1",
- "tslib": "^1.10.0"
- },
+ "license": "MIT",
"dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.21",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "rtlcss": "bin/rtlcss.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
}
},
- "popmotion-pose": {
- "version": "3.4.11",
- "resolved": "https://registry.npmjs.org/popmotion-pose/-/popmotion-pose-3.4.11.tgz",
- "integrity": "sha512-KjaevePyC1+Q3ylIcBO3YMhCouE1a/3bvtBXThrwz44fw1yXCUQagPJGkGirXI/J1xF+w3Lx3bpkkgwArizpEQ==",
+ "node_modules/run-applescript": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
+ "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
"dev": true,
- "requires": {
- "@popmotion/easing": "^1.0.1",
- "hey-listen": "^1.0.5",
- "popmotion": "^8.7.1",
- "pose-core": "^2.1.1",
- "style-value-types": "^3.0.6",
- "ts-essentials": "^1.0.3",
- "tslib": "^1.10.0"
+ "engines": {
+ "node": ">=18"
},
- "dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "portfinder": {
- "version": "1.0.25",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz",
- "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==",
- "dev": true,
- "requires": {
- "async": "^2.6.2",
- "debug": "^3.1.1",
- "mkdirp": "^0.5.1"
- },
- "dependencies": {
- "async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
- "dev": true,
- "requires": {
- "lodash": "^4.17.14"
- }
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
},
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
}
},
- "pose-core": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/pose-core/-/pose-core-2.1.1.tgz",
- "integrity": "sha512-fV1sDfu80debHmKerikypqGoORMEUHVwGh/BlWnqUSmmzQGYIg8neDrdwe66hFeRO+adr2qS4ZERSu/ZVjOiSQ==",
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
- "requires": {
- "@types/invariant": "^2.2.29",
- "@types/node": "^10.0.5",
- "hey-listen": "^1.0.5",
- "rollup-plugin-typescript2": "^0.25.2",
- "tslib": "^1.10.0",
- "typescript": "^3.7.2"
- },
+ "license": "Apache-2.0",
"dependencies": {
- "@types/node": {
- "version": "10.17.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.5.tgz",
- "integrity": "sha512-RElZIr/7JreF1eY6oD5RF3kpmdcreuQPjg5ri4oQ5g9sq7YWU8HkfB3eH8GwAwxf5OaCh0VPi7r4N/yoTGelrA==",
- "dev": true
- },
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- },
- "typescript": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
- "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
- "dev": true
- }
+ "tslib": "^2.1.0"
}
},
- "posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-array-concat/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
- "postcss": {
- "version": "7.0.27",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
- "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.2",
- "source-map": "^0.6.1",
- "supports-color": "^6.1.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
},
- "supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
- }
+ ]
},
- "postcss-calc": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz",
- "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==",
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
"dev": true,
- "requires": {
- "postcss": "^7.0.27",
- "postcss-selector-parser": "^6.0.2",
- "postcss-value-parser": "^4.0.2"
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "postcss-colormin": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
- "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
+ "node_modules/safe-push-apply/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
"dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "color": "^3.0.0",
- "has": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "postcss-convert-values": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
- "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/sax": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
+ "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/sazerac": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/sazerac/-/sazerac-2.0.0.tgz",
+ "integrity": "sha512-+oVUuqwbHf9aNJiT2WBoCYWmQy02AiBcbW1jxZUDBrrXleYRBat4qwbnwTXD3lyOVUk4F27cR8ArHZkCvjuSzw==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "deep-eql": "^4.0.0",
+ "lodash.at": "^4.6.0",
+ "lodash.concat": "^4.5.0",
+ "lodash.filter": "^4.6.0",
+ "lodash.isfunction": "^3.0.9",
+ "lodash.isobject": "^3.0.2",
+ "lodash.isstring": "^4.0.1",
+ "lodash.isundefined": "^3.0.1",
+ "lodash.keys": "^4.2.0",
+ "lodash.toarray": "^4.4.0",
+ "sprintf-js": "^1.1.2"
}
},
- "postcss-discard-comments": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
- "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
- }
+ "license": "MIT"
},
- "postcss-discard-duplicates": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
- "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
+ "node_modules/schema-dts": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz",
+ "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
- }
+ "license": "Apache-2.0"
},
- "postcss-discard-empty": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
- "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
+ "node_modules/schema-utils": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
- "postcss-discard-overridden": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
- "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
+ "node_modules/schema-utils/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
}
},
- "postcss-flexbugs-fixes": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.0.tgz",
- "integrity": "sha512-QRE0n3hpkxxS/OGvzOa+PDuy4mh/Jg4o9ui22/ko5iGYOG3M5dfJabjnAZjTdh2G9F85c7Hv8hWcEDEKW/xceQ==",
+ "node_modules/schema-utils/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"dev": true,
- "requires": {
- "postcss": "^7.0.26"
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
}
},
- "postcss-load-config": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
- "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
+ "node_modules/schema-utils/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
- "requires": {
- "cosmiconfig": "^5.0.0",
- "import-cwd": "^2.0.0"
- }
+ "license": "MIT"
},
- "postcss-loader": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
- "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+ "node_modules/search-insights": {
+ "version": "2.17.3",
+ "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz",
+ "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==",
"dev": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "postcss": "^7.0.0",
- "postcss-load-config": "^2.0.0",
- "schema-utils": "^1.0.0"
- },
- "dependencies": {
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- }
- }
+ "license": "MIT",
+ "peer": true
},
- "postcss-merge-longhand": {
- "version": "4.0.11",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
- "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
+ "node_modules/section-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
+ "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
"dev": true,
- "requires": {
- "css-color-names": "0.0.4",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0",
- "stylehacks": "^4.0.0"
- },
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "extend-shallow": "^2.0.1",
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "postcss-merge-rules": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
- "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
- "dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "caniuse-api": "^3.0.0",
- "cssnano-util-same-parent": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-selector-parser": "^3.0.0",
- "vendors": "^1.0.0"
- },
- "dependencies": {
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "postcss-selector-parser": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
- "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
- }
- }
- }
+ "node_modules/select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg=="
},
- "postcss-minify-font-values": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
- "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
+ "node_modules/selfsigned": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz",
+ "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "@peculiar/x509": "^1.14.2",
+ "pkijs": "^3.3.3"
+ },
+ "engines": {
+ "node": ">=18"
}
},
- "postcss-minify-gradients": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
- "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
- "dev": true,
- "requires": {
- "cssnano-util-get-arguments": "^4.0.0",
- "is-color-stop": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
},
- "dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "engines": {
+ "node": ">=10"
}
},
- "postcss-minify-params": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
- "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
+ "node_modules/semver-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
+ "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
"dev": true,
- "requires": {
- "alphanum-sort": "^1.0.0",
- "browserslist": "^4.0.0",
- "cssnano-util-get-arguments": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0",
- "uniqs": "^2.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
- }
- },
- "postcss-minify-selectors": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
- "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
- "dev": true,
- "requires": {
- "alphanum-sort": "^1.0.0",
- "has": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-selector-parser": "^3.0.0"
- },
- "dependencies": {
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "postcss-selector-parser": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
- "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
- }
- }
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "postcss-modules-extract-imports": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz",
- "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==",
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
"dev": true,
- "requires": {
- "postcss": "^6.0.1"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "postcss-modules-local-by-default": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz",
- "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=",
- "dev": true,
- "requires": {
- "css-selector-tokenizer": "^0.7.0",
- "postcss": "^6.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "postcss-modules-scope": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz",
- "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=",
- "dev": true,
- "requires": {
- "css-selector-tokenizer": "^0.7.0",
- "postcss": "^6.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "postcss-modules-values": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz",
- "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=",
- "dev": true,
- "requires": {
- "icss-replace-symbols": "^1.1.0",
- "postcss": "^6.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "requires": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
}
},
- "postcss-normalize-charset": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
- "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0"
- }
+ "license": "MIT"
},
- "postcss-normalize-display-values": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
- "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
+ "node_modules/send/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true,
- "requires": {
- "cssnano-util-get-match": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
- "dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "postcss-normalize-positions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
- "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
- "requires": {
- "cssnano-util-get-arguments": "^4.0.0",
- "has": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
+ "license": "MIT"
+ },
+ "node_modules/serialize-error": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz",
+ "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==",
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "postcss-normalize-repeat-style": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
- "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
- "dev": true,
- "requires": {
- "cssnano-util-get-arguments": "^4.0.0",
- "cssnano-util-get-match": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
+ "node_modules/serialize-error/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
},
- "dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "postcss-normalize-string": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
- "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
+ "node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
- "requires": {
- "has": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "randombytes": "^2.1.0"
}
},
- "postcss-normalize-timing-functions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
- "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
+ "node_modules/serve-handler": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz",
+ "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==",
"dev": true,
- "requires": {
- "cssnano-util-get-match": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "mime-types": "2.1.18",
+ "minimatch": "3.1.5",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "3.3.0",
+ "range-parser": "1.2.0"
}
},
- "postcss-normalize-unicode": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
- "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
+ "node_modules/serve-handler/node_modules/content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
"dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
- "dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
}
},
- "postcss-normalize-url": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
- "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
+ "node_modules/serve-handler/node_modules/mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
"dev": true,
- "requires": {
- "is-absolute-url": "^2.0.0",
- "normalize-url": "^3.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
- "dependencies": {
- "is-absolute-url": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
- "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
- "dev": true
- },
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
}
},
- "postcss-normalize-whitespace": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
- "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
+ "node_modules/serve-handler/node_modules/mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"dev": true,
- "requires": {
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "mime-db": "~1.33.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "postcss-ordered-values": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
- "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
+ "node_modules/serve-handler/node_modules/path-to-regexp": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz",
+ "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==",
"dev": true,
- "requires": {
- "cssnano-util-get-arguments": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
- "dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
- }
+ "license": "MIT"
},
- "postcss-reduce-initial": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
- "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
+ "node_modules/serve-handler/node_modules/range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
"dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "caniuse-api": "^3.0.0",
- "has": "^1.0.0",
- "postcss": "^7.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
}
},
- "postcss-reduce-transforms": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
- "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
+ "node_modules/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
"dev": true,
- "requires": {
- "cssnano-util-get-match": "^4.0.0",
- "has": "^1.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "postcss-selector-parser": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
- "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "node_modules/serve-index/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
- "requires": {
- "cssesc": "^3.0.0",
- "indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
- }
- },
- "postcss-svgo": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz",
- "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==",
- "dev": true,
- "requires": {
- "is-svg": "^3.0.0",
- "postcss": "^7.0.0",
- "postcss-value-parser": "^3.0.0",
- "svgo": "^1.0.0"
- },
- "dependencies": {
- "is-svg": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
- "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==",
- "dev": true,
- "requires": {
- "html-comment-regex": "^1.1.0"
- }
- },
- "postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
- }
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
}
},
- "postcss-unique-selectors": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
- "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
+ "node_modules/serve-index/node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
"dev": true,
- "requires": {
- "alphanum-sort": "^1.0.0",
- "postcss": "^7.0.0",
- "uniqs": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "postcss-value-parser": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz",
- "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==",
- "dev": true
- },
- "prelude-ls": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+ "node_modules/serve-index/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true,
+ "license": "ISC"
},
- "prepend-http": {
+ "node_modules/serve-index/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
- "dev": true
- },
- "prettier": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
- "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
- "dev": true
- },
- "pretty-bytes": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz",
- "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg=="
- },
- "pretty-error": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
- "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true,
- "requires": {
- "renderkid": "^2.0.1",
- "utila": "~0.4"
- }
+ "license": "MIT"
},
- "prettyjson": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz",
- "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=",
+ "node_modules/serve-index/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
"dev": true,
- "requires": {
- "colors": "^1.1.2",
- "minimist": "^1.2.0"
- }
- },
- "priorityqueuejs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz",
- "integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg="
- },
- "private": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
- "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
- "dev": true
+ "license": "ISC"
},
- "process": {
- "version": "0.11.10",
- "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
- "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
- "dev": true
- },
- "process-nextick-args": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
- "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
- "dev": true
- },
- "process-on-spawn": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
- "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==",
+ "node_modules/serve-index/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
"dev": true,
- "requires": {
- "fromentries": "^1.2.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
}
},
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "dev": true
- },
- "prom-client": {
- "version": "11.5.3",
- "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz",
- "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==",
- "requires": {
- "tdigest": "^0.1.1"
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "promise": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/promise/-/promise-7.0.4.tgz",
- "integrity": "sha1-Nj6EpMNsg1a4kP7WLJHOhdAu1Tk=",
- "requires": {
- "asap": "~2.0.3"
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "promise-inflight": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
- "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
- "dev": true
- },
- "promise-polyfill": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.0.2.tgz",
- "integrity": "sha1-2chtPcTcLfkBboiUbe/Wm0m0EWI=",
- "dev": true
- },
- "promise-queue": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz",
- "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=",
- "dev": true
- },
- "prompts": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz",
- "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==",
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dev": true,
- "requires": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.4"
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
"dev": true,
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.8.1"
- },
+ "license": "MIT",
"dependencies": {
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- },
- "react-is": {
- "version": "16.8.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz",
- "integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA==",
- "dev": true
- }
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "prop-types-exact": {
+ "node_modules/setprototypeof": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz",
- "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
"dev": true,
- "requires": {
- "has": "^1.0.3",
- "object.assign": "^4.1.0",
- "reflect.ownkeys": "^0.2.0"
- },
"dependencies": {
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- }
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "propagate": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
- "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
- "dev": true
- },
- "protocols": {
- "version": "1.4.7",
- "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz",
- "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==",
- "dev": true
- },
- "proxy-addr": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
- "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
"dev": true,
- "requires": {
- "forwarded": "~0.1.2",
- "ipaddr.js": "1.9.1"
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
- "dev": true
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
},
- "ps-tree": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
- "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
+ "node_modules/shell-quote": {
+ "version": "1.8.4",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
+ "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
"dev": true,
- "requires": {
- "event-stream": "=3.3.4"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
- "dev": true
- },
- "psl": {
- "version": "1.1.29",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
- "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
- },
- "pstree.remy": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
- "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==",
- "dev": true
+ "node_modules/shelljs": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+ "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "glob": "^7.0.0",
+ "interpret": "^1.0.0",
+ "rechoir": "^0.6.2"
+ },
+ "bin": {
+ "shjs": "bin/shjs"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "public-encrypt": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
- "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "node_modules/shelljs/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
- "requires": {
- "bn.js": "^4.1.0",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "parse-asn1": "^5.0.0",
- "randombytes": "^2.0.1",
- "safe-buffer": "^5.1.2"
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
},
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/should": {
+ "version": "13.2.3",
+ "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+ "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
- "dev": true
- }
+ "should-equal": "^2.0.0",
+ "should-format": "^3.0.3",
+ "should-type": "^1.4.0",
+ "should-type-adaptors": "^1.0.1",
+ "should-util": "^1.0.0"
}
},
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "node_modules/should-equal": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+ "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
"dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
+ "license": "MIT",
+ "dependencies": {
+ "should-type": "^1.4.0"
}
},
- "pumpify": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
- "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "node_modules/should-format": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+ "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==",
"dev": true,
- "requires": {
- "duplexify": "^3.6.0",
- "inherits": "^2.0.3",
- "pump": "^2.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "pump": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
- "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- }
+ "should-type": "^1.3.0",
+ "should-type-adaptors": "^1.0.1"
}
},
- "punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
- "dev": true
+ "node_modules/should-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+ "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==",
+ "dev": true,
+ "license": "MIT"
},
- "pupa": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz",
- "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==",
+ "node_modules/should-type-adaptors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+ "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
"dev": true,
- "requires": {
- "escape-goat": "^2.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "should-type": "^1.3.0",
+ "should-util": "^1.0.0"
}
},
- "q": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
- "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
- "dev": true
- },
- "qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+ "node_modules/should-util": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz",
+ "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==",
+ "dev": true,
+ "license": "MIT"
},
- "query-string": {
- "version": "6.12.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.12.1.tgz",
- "integrity": "sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA==",
- "requires": {
- "decode-uri-component": "^0.2.0",
- "split-on-first": "^1.0.0",
- "strict-uri-encode": "^2.0.0"
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
- "dev": true
- },
- "querystring-es3": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
- "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
- "dev": true
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "querystringify": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
- "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
- "dev": true
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "quick-lru": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz",
- "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
- "dev": true
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "quote": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/quote/-/quote-0.4.0.tgz",
- "integrity": "sha1-EIOSF/bBNiuJGUBE0psjP9fzLwE=",
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
- "r-json": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.2.9.tgz",
- "integrity": "sha512-E5u25XBE7PpZmH5XwtthAmNvSLMTygDQMpcPtCTUBdvwPaqgIYJrxlRQJhG55Sgz7uC0Tuyh5nqNrsDT3uiefA=="
- },
- "raf": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
- "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+ "node_modules/simple-git-hooks": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.13.1.tgz",
+ "integrity": "sha512-WszCLXwT4h2k1ufIXAgsbiTOazqqevFCIncOuUBZJ91DdvWcC5+OFkluWRQPrcuSYd8fjq+o2y1QfWqYMoAToQ==",
"dev": true,
- "requires": {
- "performance-now": "^2.1.0"
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "simple-git-hooks": "cli.js"
}
},
- "railroad-diagrams": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
- "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
- "dev": true
- },
- "ramda": {
- "version": "0.26.1",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
- "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==",
- "dev": true
+ "node_modules/simple-icons": {
+ "version": "16.26.0",
+ "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.26.0.tgz",
+ "integrity": "sha512-T9rNJtyOshULM8heLlvrZY346g9zOgZILQ3vxP+FWcX13RhaOLez7YM/hNCEx3b5gJckSCNDoNsjZuDncSMYSQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/simple-icons"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/simple-icons"
+ }
+ ],
+ "license": "CC0-1.0",
+ "engines": {
+ "node": ">=0.12.18"
+ }
},
- "randexp": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
- "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true,
- "requires": {
- "discontinuous-range": "1.0.0",
- "ret": "~0.1.10"
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "node_modules/sinon": {
+ "version": "22.1.0",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.1.0.tgz",
+ "integrity": "sha512-n1ajF2rBWMTtEwbKcw4UdFg4nCnDdq/U6RDoxtOd7oapOlRoJ5ynwFx60owROyhDpA9QhMZi0pCO/xtmwFjG7w==",
"dev": true,
- "requires": {
- "safe-buffer": "^5.1.0"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1",
+ "@sinonjs/fake-timers": "^15.4.0",
+ "@sinonjs/samsam": "^10.0.2",
+ "diff": "^9.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/sinon"
}
},
- "randomfill": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
- "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "node_modules/sinon-chai": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-4.0.1.tgz",
+ "integrity": "sha512-xMKEEV3cYHC1G+boyr7QEqi80gHznYsxVdC9CdjP5JnCWz/jPGuXQzJz3PtBcb0CcHAxar15Y5sjLBoAs6a0yA==",
"dev": true,
- "requires": {
- "randombytes": "^2.0.5",
- "safe-buffer": "^5.1.0"
+ "license": "(BSD-2-Clause OR WTFPL)",
+ "peerDependencies": {
+ "chai": "^5.0.0 || ^6.0.0",
+ "sinon": ">=4.0.0"
}
},
- "range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "dev": true
- },
- "raw-body": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
- "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "node_modules/sirv": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+ "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
"dev": true,
- "requires": {
- "bytes": "3.1.0",
- "http-errors": "1.7.2",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
}
},
- "raw-loader": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
- "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "node_modules/sitemap": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.3.tgz",
+ "integrity": "sha512-tAjEd+wt/YwnEbfNB2ht51ybBJxbEWwe5ki/Z//Wh0rpBFTCUSj46GnxUKEWzhfuJTsee8x3lybHxFgUMig2hw==",
"dev": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^17.0.5",
+ "@types/sax": "^1.2.1",
+ "arg": "^5.0.0",
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "sitemap": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=12.0.0",
+ "npm": ">=5.6.0"
}
},
- "react": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
- "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
+ "node_modules/sitemap/node_modules/@types/node": {
+ "version": "17.0.45",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
+ "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
"dev": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2"
- }
- },
- "react-dev-utils": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-4.2.3.tgz",
- "integrity": "sha512-uvmkwl5uMexCmC0GUv1XGQP0YjfYePJufGg4YYiukhqk2vN1tQxwWJIBERqhOmSi80cppZg8mZnPP/kOMf1sUQ==",
- "dev": true,
- "requires": {
- "address": "1.0.3",
- "babel-code-frame": "6.26.0",
- "chalk": "1.1.3",
- "cross-spawn": "5.1.0",
- "detect-port-alt": "1.1.3",
- "escape-string-regexp": "1.0.5",
- "filesize": "3.5.11",
- "global-modules": "1.0.0",
- "gzip-size": "3.0.0",
- "inquirer": "3.3.0",
- "is-root": "1.0.0",
- "opn": "5.1.0",
- "react-error-overlay": "^3.0.0",
- "recursive-readdir": "2.2.1",
- "shell-quote": "1.6.1",
- "sockjs-client": "1.1.4",
- "strip-ansi": "3.0.1",
- "text-table": "0.2.0"
- },
- "dependencies": {
- "address": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
- "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==",
- "dev": true
- },
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "chardet": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
- "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
- "dev": true
- },
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "detect-port-alt": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.3.tgz",
- "integrity": "sha1-pNLwYddXoDTs83xRQmCph1DysTE=",
- "dev": true,
- "requires": {
- "address": "^1.0.1",
- "debug": "^2.6.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "eventsource": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz",
- "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=",
- "dev": true,
- "requires": {
- "original": ">=0.0.5"
- }
- },
- "external-editor": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
- "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
- "dev": true,
- "requires": {
- "chardet": "^0.4.0",
- "iconv-lite": "^0.4.17",
- "tmp": "^0.0.33"
- }
- },
- "filesize": {
- "version": "3.5.11",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz",
- "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==",
- "dev": true
- },
- "global-modules": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
- "dev": true,
- "requires": {
- "global-prefix": "^1.0.1",
- "is-windows": "^1.0.1",
- "resolve-dir": "^1.0.0"
- }
- },
- "global-prefix": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
- "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
- "dev": true,
- "requires": {
- "expand-tilde": "^2.0.2",
- "homedir-polyfill": "^1.0.1",
- "ini": "^1.3.4",
- "is-windows": "^1.0.1",
- "which": "^1.2.14"
- }
- },
- "gzip-size": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz",
- "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=",
- "dev": true,
- "requires": {
- "duplexer": "^0.1.1"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "inquirer": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
- "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^3.0.0",
- "chalk": "^2.0.0",
- "cli-cursor": "^2.1.0",
- "cli-width": "^2.0.0",
- "external-editor": "^2.0.4",
- "figures": "^2.0.0",
- "lodash": "^4.3.0",
- "mute-stream": "0.0.7",
- "run-async": "^2.2.0",
- "rx-lite": "^4.0.8",
- "rx-lite-aggregates": "^4.0.8",
- "string-width": "^2.1.0",
- "strip-ansi": "^4.0.0",
- "through": "^2.3.6"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "is-root": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz",
- "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=",
- "dev": true
- },
- "minimatch": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
- "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
- "dev": true,
- "requires": {
- "brace-expansion": "^1.0.0"
- }
- },
- "recursive-readdir": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.1.tgz",
- "integrity": "sha1-kO8jHQd4xc4JPJpI105cVCLROpk=",
- "dev": true,
- "requires": {
- "minimatch": "3.0.3"
- }
- },
- "sockjs-client": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz",
- "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=",
- "dev": true,
- "requires": {
- "debug": "^2.6.6",
- "eventsource": "0.1.6",
- "faye-websocket": "~0.11.0",
- "inherits": "^2.0.1",
- "json3": "^3.3.2",
- "url-parse": "^1.1.8"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- },
- "tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.2"
- }
- }
- }
+ "license": "MIT"
},
- "react-dom": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
- "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
+ "node_modules/skin-tone": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
+ "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
"dev": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.19.1"
- },
+ "license": "MIT",
"dependencies": {
- "scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- }
+ "unicode-emoji-modifier-base": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "react-error-overlay": {
+ "node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-3.0.0.tgz",
- "integrity": "sha512-XzgvowFrwDo6TWcpJ/WTiarb9UI6lhA4PMzS7n1joK3sHfBBBOQHUc0U4u57D6DWO9vHv6lVSWx2Q/Ymfyv4hw==",
- "dev": true
- },
- "react-fast-compare": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
- "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
- "dev": true
- },
- "react-helmet": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.1.tgz",
- "integrity": "sha512-CnwD822LU8NDBnjCpZ4ySh8L6HYyngViTZLfBBb3NjtrpN8m49clH8hidHouq20I51Y6TpCTISCBbqiY5GamwA==",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
- "requires": {
- "object-assign": "^4.1.1",
- "prop-types": "^15.5.4",
- "react-fast-compare": "^2.0.2",
- "react-side-effect": "^1.1.0"
+ "engines": {
+ "node": ">=8"
}
},
- "react-hot-loader": {
- "version": "4.12.19",
- "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.19.tgz",
- "integrity": "sha512-p8AnA4QE2GtrvkdmqnKrEiijtVlqdTIDCHZOwItkI9kW51bt5XnQ/4Anz8giiWf9kqBpEQwsmnChDCAFBRyR/Q==",
+ "node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
"dev": true,
- "requires": {
- "fast-levenshtein": "^2.0.6",
- "global": "^4.3.0",
- "hoist-non-react-statics": "^3.3.0",
- "loader-utils": "^1.1.0",
- "prop-types": "^15.6.1",
- "react-lifecycles-compat": "^3.0.4",
- "shallowequal": "^1.1.0",
- "source-map": "^0.7.3"
- },
+ "license": "MIT",
"dependencies": {
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "dev": true
- }
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
- "react-input-autosize": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz",
- "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==",
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
- "requires": {
- "prop-types": "^15.5.8"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "react-is": {
- "version": "16.8.5",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.5.tgz",
- "integrity": "sha512-sudt2uq5P/2TznPV4Wtdi+Lnq3yaYW8LfvPKLM9BKD8jJNBkxMVyB0C9/GmVhLw7Jbdmndk/73n7XQGeN9A3QQ==",
- "dev": true
- },
- "react-lifecycles-compat": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
- "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
- "dev": true
- },
- "react-modal": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
- "integrity": "sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==",
- "dev": true,
- "requires": {
- "exenv": "^1.2.0",
- "prop-types": "^15.5.10",
- "react-lifecycles-compat": "^3.0.0",
- "warning": "^4.0.3"
+ "node_modules/smol-toml": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz",
+ "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
}
},
- "react-pose": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/react-pose/-/react-pose-4.0.10.tgz",
- "integrity": "sha512-OKc5oqKw+nL9FvIokxn8MmaAmkNsWv64hLX9xWWcMWXSgEo745hzYUqDn2viMJ97mf76oPy6Vc+BS4k6Kwj78g==",
+ "node_modules/snake-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
+ "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
"dev": true,
- "requires": {
- "@emotion/is-prop-valid": "^0.7.3",
- "hey-listen": "^1.0.5",
- "popmotion-pose": "^3.4.10",
- "tslib": "^1.10.0"
- },
+ "license": "MIT",
"dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
}
},
- "react-reconciler": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.24.0.tgz",
- "integrity": "sha512-gAGnwWkf+NOTig9oOowqid9O0HjTDC+XVGBCAmJYYJ2A2cN/O4gDdIuuUQjv8A4v6GDwVfJkagpBBLW5OW9HSw==",
+ "node_modules/snap-shot-compare": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/snap-shot-compare/-/snap-shot-compare-3.0.0.tgz",
+ "integrity": "sha512-bdwNOAGuKwPU+qsn0ASxTv+QfkXU+3VmkcDOkt965tes+JQQc8d6SfoLiEiRVhCey4v+ip2IjNUSbZm5nnkI9g==",
"dev": true,
- "optional": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.18.0"
+ "dependencies": {
+ "check-more-types": "2.24.0",
+ "debug": "4.1.1",
+ "disparity": "3.0.0",
+ "folktale": "2.3.2",
+ "lazy-ass": "1.6.0",
+ "strip-ansi": "5.2.0",
+ "variable-diff": "1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "react-refresh": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.7.2.tgz",
- "integrity": "sha512-u5l7fhAJXecWUJzVxzMRU2Zvw8m4QmDNHlTrT5uo3KBlYBhmChd7syAakBoay1yIiVhx/8Fi7a6v6kQZfsw81Q==",
- "dev": true
- },
- "react-select": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.1.0.tgz",
- "integrity": "sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==",
+ "node_modules/snap-shot-compare/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.4.4",
- "@emotion/cache": "^10.0.9",
- "@emotion/core": "^10.0.9",
- "@emotion/css": "^10.0.9",
- "memoize-one": "^5.0.0",
- "prop-types": "^15.6.0",
- "react-input-autosize": "^2.2.2",
- "react-transition-group": "^4.3.0"
+ "engines": {
+ "node": ">=6"
}
},
- "react-side-effect": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz",
- "integrity": "sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw==",
+ "node_modules/snap-shot-compare/node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"dev": true,
- "requires": {
- "exenv": "^1.2.1",
- "shallowequal": "^1.0.1"
+ "dependencies": {
+ "ms": "^2.1.1"
}
},
- "react-test-renderer": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.12.0.tgz",
- "integrity": "sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w==",
+ "node_modules/snap-shot-compare/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
- "requires": {
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "react-is": "^16.8.6",
- "scheduler": "^0.18.0"
- },
"dependencies": {
- "react-is": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
- "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
- "dev": true
- }
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "react-transition-group": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
- "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
+ "node_modules/snap-shot-core": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/snap-shot-core/-/snap-shot-core-10.2.4.tgz",
+ "integrity": "sha512-A7tkcfmvnRKge4VzFLAWA4UYMkvFY4TZKyL+D6hnHjI3HJ4pTepjG5DfR2ACeDKMzCSTQ5EwR2iOotI+Z37zsg==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.5.5",
- "dom-helpers": "^5.0.1",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.6.2"
+ "dependencies": {
+ "arg": "4.1.3",
+ "check-more-types": "2.24.0",
+ "common-tags": "1.8.0",
+ "debug": "4.3.1",
+ "escape-quotes": "1.0.2",
+ "folktale": "2.3.2",
+ "is-ci": "2.0.0",
+ "jsesc": "2.5.2",
+ "lazy-ass": "1.6.0",
+ "mkdirp": "1.0.4",
+ "pluralize": "8.0.0",
+ "quote": "0.4.0",
+ "ramda": "0.27.1"
},
- "dependencies": {
- "csstype": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz",
- "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==",
- "dev": true
- },
- "dom-helpers": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
- "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.6.3",
- "csstype": "^2.6.7"
- },
- "dependencies": {
- "@babel/runtime": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
- "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- }
- }
- },
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
- "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
- "dev": true
- }
- }
- },
- "read": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
- "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
- "dev": true,
- "requires": {
- "mute-stream": "~0.0.4"
+ "bin": {
+ "resave-snapshots": "bin/resave-snapshots.js"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "read-all-stdin-sync": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/read-all-stdin-sync/-/read-all-stdin-sync-1.0.5.tgz",
- "integrity": "sha1-Kj0EBEYk+3PJRbzfcEEsPrEd7yI=",
+ "node_modules/snap-shot-core/node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
- "read-pkg": {
+ "node_modules/snap-shot-core/node_modules/ci-info": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
- "dev": true,
- "requires": {
- "load-json-file": "^2.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
- }
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
- "read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "node_modules/snap-shot-core/node_modules/common-tags": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
+ "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
"dev": true,
- "requires": {
- "find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
+ "engines": {
+ "node": ">=4.0.0"
}
},
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- },
+ "node_modules/snap-shot-core/node_modules/debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
"dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
}
}
},
- "readdirp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
- "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "node_modules/snap-shot-core/node_modules/is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.11",
- "micromatch": "^3.1.10",
- "readable-stream": "^2.0.2"
+ "dependencies": {
+ "ci-info": "^2.0.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
}
},
- "readline-sync": {
- "version": "1.4.10",
- "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
- "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==",
+ "node_modules/snap-shot-core/node_modules/ramda": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz",
+ "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==",
"dev": true
},
- "recursive-readdir": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
- "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
+ "node_modules/snap-shot-it": {
+ "version": "7.9.10",
+ "resolved": "https://registry.npmjs.org/snap-shot-it/-/snap-shot-it-7.9.10.tgz",
+ "integrity": "sha512-9USmsI2jc2kQslRhqkzFX+2K23o6v3VEzlQHwUNpZbbnEdU0ommReg2+Px7260sd+P/6CtaDx+LXadzt4caMVQ==",
"dev": true,
- "requires": {
- "minimatch": "3.0.4"
+ "dependencies": {
+ "@bahmutov/data-driven": "1.0.0",
+ "check-more-types": "2.24.0",
+ "common-tags": "1.8.2",
+ "debug": "4.3.4",
+ "has-only": "1.1.1",
+ "its-name": "1.0.0",
+ "lazy-ass": "1.6.0",
+ "pluralize": "8.0.0",
+ "ramda": "0.28.0",
+ "snap-shot-compare": "3.0.0",
+ "snap-shot-core": "10.2.4"
+ },
+ "engines": {
+ "node": ">=6"
}
},
- "redent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz",
- "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=",
- "dev": true,
- "requires": {
- "indent-string": "^3.0.0",
- "strip-indent": "^2.0.0"
+ "node_modules/socket.io": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.1.tgz",
+ "integrity": "sha512-eaTE4tBKRD6RFoetquMbxgvcpvoDtRyIlkIMI/SMK2bsKvbENTsDeeu4GJ/z9c90yOWxB7b/eC+yKLPbHnH6bA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "~4.1.0",
+ "engine.io": "~3.6.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.5.0",
+ "socket.io-parser": "~3.4.0"
}
},
- "redis-commands": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz",
- "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg=="
- },
- "redis-errors": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
- "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
- },
- "redis-parser": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
- "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
- "requires": {
- "redis-errors": "^1.0.0"
- }
+ "node_modules/socket.io-adapter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
- "redis-server": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/redis-server/-/redis-server-1.2.2.tgz",
- "integrity": "sha512-pOaSIeSMVFkEFIuaMtpQ3TOr3uI4sUmEHm4ofGks5vTPRseHUszxyIlC70IFjUR9qSeH8o/ARZEM8dqcJmgGJw==",
- "dev": true,
- "requires": {
- "promise-queue": "^2.2.5"
+ "node_modules/socket.io-client": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz",
+ "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==",
+ "dependencies": {
+ "backo2": "1.0.2",
+ "component-bind": "1.0.0",
+ "component-emitter": "~1.3.0",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.5.0",
+ "has-binary2": "~1.0.2",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.6",
+ "parseuri": "0.0.6",
+ "socket.io-parser": "~3.3.0",
+ "to-array": "0.1.4"
}
},
- "redux": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
- "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.4.0",
- "symbol-observable": "^1.2.0"
- },
+ "node_modules/socket.io-client/node_modules/debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- }
+ "ms": "2.0.0"
}
},
- "redux-thunk": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
- "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==",
- "dev": true
- },
- "reflect.ownkeys": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
- "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=",
- "dev": true
+ "node_modules/socket.io-client/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
- "regenerate": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
- "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
- "dev": true
+ "node_modules/socket.io-client/node_modules/socket.io-parser": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.5.tgz",
+ "integrity": "sha512-pn+xG/oVnofxqteOawycpHw9QKclpNRa+Z7RW0vZmrIpkgZOVSVzBvY1YGR+p9kIEZXkeAjpHa21wRNLCZ6UAA==",
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "~1.3.0",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ }
},
- "regenerate-unicode-properties": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz",
- "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==",
- "dev": true,
- "requires": {
- "regenerate": "^1.4.0"
+ "node_modules/socket.io-parser": {
+ "version": "3.4.5",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.5.tgz",
+ "integrity": "sha512-nrhzTtwpgt8tN+la+9Tpzj2epV7FMtr+9lqcfpwopjoxBfUXvBR7TWwF+/UT7RiTbFcHiEJdvRZ7yptJgZxo1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "1.2.1",
+ "debug": "~4.1.0",
+ "isarray": "2.0.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
}
},
- "regenerator-runtime": {
- "version": "0.12.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
- "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==",
- "dev": true
+ "node_modules/socket.io-parser/node_modules/component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA=="
},
- "regenerator-transform": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz",
- "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==",
- "dev": true,
- "requires": {
- "private": "^0.1.6"
+ "node_modules/socket.io-parser/node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dependencies": {
+ "ms": "^2.1.1"
}
},
- "regex-escape": {
- "version": "3.4.9",
- "resolved": "https://registry.npmjs.org/regex-escape/-/regex-escape-3.4.9.tgz",
- "integrity": "sha512-Cv9rjwyQwVhn3L097ysanWsEElurmxDj6Cc4Ut23z7e6hzRbrNvF3Le7yAciMfuzyb0sZwSr0ZHunMNCIoy2/g=="
- },
- "regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "dev": true,
- "requires": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
- },
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
+ "node_modules/socket.io/node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dependencies": {
+ "ms": "^2.1.1"
}
},
- "regexp.prototype.flags": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
- "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+ "node_modules/sockjs": {
+ "version": "0.3.24",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+ "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
+ "license": "MIT",
"dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
+ "faye-websocket": "^0.11.3",
+ "uuid": "^8.3.2",
+ "websocket-driver": "^0.7.4"
}
},
- "regexpp": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
- "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
- "dev": true
- },
- "regexpu-core": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz",
- "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==",
+ "node_modules/sort-css-media-queries": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz",
+ "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==",
"dev": true,
- "requires": {
- "regenerate": "^1.4.0",
- "regenerate-unicode-properties": "^8.1.0",
- "regjsgen": "^0.5.0",
- "regjsparser": "^0.6.0",
- "unicode-match-property-ecmascript": "^1.0.4",
- "unicode-match-property-value-ecmascript": "^1.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.3.0"
}
},
- "regextras": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.0.tgz",
- "integrity": "sha512-ds+fL+Vhl918gbAUb0k2gVKbTZLsg84Re3DI6p85Et0U0tYME3hyW4nMK8Px4dtDaBA2qNjvG5uWyW7eK5gfmw==",
- "dev": true
- },
- "registry-auth-token": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz",
- "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==",
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
- "requires": {
- "rc": "^1.2.8"
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "registry-url": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
- "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
- "requires": {
- "rc": "^1.2.8"
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "regjsgen": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz",
- "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==",
- "dev": true
- },
- "regjsparser": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz",
- "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==",
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
- "requires": {
- "jsesc": "~0.5.0"
- },
"dependencies": {
- "jsesc": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
- "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
- "dev": true
- }
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
}
},
- "release-zalgo": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
- "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=",
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
"dev": true,
- "requires": {
- "es6-error": "^4.0.1"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
- "dev": true
- },
- "renderkid": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz",
- "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==",
+ "node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
"dev": true,
- "requires": {
- "css-select": "^1.1.0",
- "dom-converter": "^0.2",
- "htmlparser2": "^3.3.0",
- "strip-ansi": "^3.0.0",
- "utila": "^0.4.0"
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
}
},
- "repeat-element": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
- "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
- "repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.13",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
+ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
"dev": true
},
- "request": {
- "version": "2.88.2",
- "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
- "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
- "requires": {
- "aws-sign2": "~0.7.0",
- "aws4": "^1.8.0",
- "caseless": "~0.12.0",
- "combined-stream": "~1.0.6",
- "extend": "~3.0.2",
- "forever-agent": "~0.6.1",
- "form-data": "~2.3.2",
- "har-validator": "~5.1.3",
- "http-signature": "~1.2.0",
- "is-typedarray": "~1.0.0",
- "isstream": "~0.1.2",
- "json-stringify-safe": "~5.0.1",
- "mime-types": "~2.1.19",
- "oauth-sign": "~0.9.0",
- "performance-now": "^2.1.0",
- "qs": "~6.5.2",
- "safe-buffer": "^5.1.2",
- "tough-cookie": "~2.5.0",
- "tunnel-agent": "^0.6.0",
- "uuid": "^3.3.2"
- },
+ "node_modules/spdy": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
"dependencies": {
- "mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
- },
- "mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
- "requires": {
- "mime-db": "1.43.0"
- }
- },
- "safe-buffer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
- "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
- }
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "vendor/http-deceiver",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
- "request-progress": {
+ "node_modules/spdy-transport": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
- "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=",
- "dev": true,
- "requires": {
- "throttleit": "^1.0.0"
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dependencies": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "engines": {
+ "node": ">= 10.x"
}
},
- "request-promise-core": {
+ "node_modules/sprintf-js": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
- "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true,
- "requires": {
- "lodash": "^4.17.15"
- }
+ "license": "BSD-3-Clause"
},
- "request-promise-native": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
- "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
+ "node_modules/srcset": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz",
+ "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==",
"dev": true,
- "requires": {
- "request-promise-core": "1.1.3",
- "stealthy-require": "^1.1.1",
- "tough-cookie": "^2.3.3"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
- "dev": true
- },
- "require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "dev": true
- },
- "require-hacker": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/require-hacker/-/require-hacker-3.0.1.tgz",
- "integrity": "sha1-CHm+Bn/PBnUwZlvM5MiayBqHBHc=",
+ "node_modules/sshpk": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+ "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
"dev": true,
- "requires": {
- "babel-runtime": "^6.6.1",
- "colors": "^1.1.2"
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
- "dev": true
- },
- "requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
- "dev": true
+ "node_modules/start-server-and-test": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-3.0.11.tgz",
+ "integrity": "sha512-NRapOwJl6jr1DNSaQ+SRukHI2OKcFZA2Iv2tfTW9fI/S+6YmJGiwacR+0MG3o5p39lY4xWUOE5JFkKJBZUjxuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arg": "5.0.2",
+ "check-more-types": "2.24.0",
+ "debug": "4.4.3",
+ "execa": "5.1.1",
+ "lazy-ass": "2.0.3",
+ "tree-kill": "1.2.2",
+ "wait-on": "9.0.10"
+ },
+ "bin": {
+ "server-test": "src/bin/start.js",
+ "start-server-and-test": "src/bin/start.js",
+ "start-test": "src/bin/start.js"
+ },
+ "engines": {
+ "node": "^22 || >=24"
+ }
},
- "requizzle": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz",
- "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==",
+ "node_modules/start-server-and-test/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
- "requires": {
- "lodash": "^4.17.14"
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "resolve": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
- "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+ "node_modules/start-server-and-test/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
"dev": true,
- "requires": {
- "path-parse": "^1.0.5"
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "resolve-cwd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
- "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "node_modules/start-server-and-test/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true,
- "requires": {
- "resolve-from": "^3.0.0"
+ "engines": {
+ "node": ">=10"
},
- "dependencies": {
- "resolve-from": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "resolve-dir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
- "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
- "dev": true,
- "requires": {
- "expand-tilde": "^2.0.0",
- "global-modules": "^1.0.0"
- },
- "dependencies": {
- "global-modules": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
- "dev": true,
- "requires": {
- "global-prefix": "^1.0.1",
- "is-windows": "^1.0.1",
- "resolve-dir": "^1.0.0"
- }
- },
- "global-prefix": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
- "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
- "dev": true,
- "requires": {
- "expand-tilde": "^2.0.2",
- "homedir-polyfill": "^1.0.1",
- "ini": "^1.3.4",
- "is-windows": "^1.0.1",
- "which": "^1.2.14"
- }
- }
- }
- },
- "resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true
- },
- "resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
- "dev": true
- },
- "responselike": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
- "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+ "node_modules/start-server-and-test/node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
"dev": true,
- "requires": {
- "lowercase-keys": "^1.0.0"
+ "engines": {
+ "node": ">=10.17.0"
}
},
- "restore-cursor": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
- "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "node_modules/start-server-and-test/node_modules/lazy-ass": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-2.0.3.tgz",
+ "integrity": "sha512-/O3/DoQmI1XAhklDvF1dAjFf/epE8u3lzOZegQfLZ8G7Ud5bTRSZiFOpukHCu6jODrCA4gtIdwUCC7htxcDACA==",
"dev": true,
- "requires": {
- "onetime": "^2.0.0",
- "signal-exit": "^3.0.2"
+ "license": "MIT",
+ "engines": {
+ "node": "> 0.8"
}
},
- "ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
- "dev": true
- },
- "retry": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
- "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
- "dev": true
+ "node_modules/start-server-and-test/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
},
- "reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true
+ "node_modules/state-local": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
+ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
+ "dev": true,
+ "license": "MIT"
},
- "rgb-regex": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
- "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
- "dev": true
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "rgba-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
- "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
- "dev": true
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
},
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
"dev": true,
- "requires": {
- "glob": "^7.1.3"
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "ripemd160": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
- "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
- "dev": true,
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1"
- }
- },
- "rollup-plugin-typescript2": {
- "version": "0.25.2",
- "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.25.2.tgz",
- "integrity": "sha512-+tpZj/ZIf2lwjyjX6xEW1S5Y38/21TB3p6poLodISIia8owMMfIKuFFnWcESE4FPBHkR8XPKqjY0PH9IUJJK+Q==",
- "dev": true,
- "requires": {
- "find-cache-dir": "^3.0.0",
- "fs-extra": "8.1.0",
- "resolve": "1.12.0",
- "rollup-pluginutils": "2.8.1",
- "tslib": "1.10.0"
- },
- "dependencies": {
- "find-cache-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.1.0.tgz",
- "integrity": "sha512-zw+EFiNBNPgI2NTrKkDd1xd7q0cs6wr/iWnr/oUkI0yF9K9GqQ+riIt4aiyFaaqpaWbxPrJXHI+QvmNUQbX+0Q==",
- "dev": true,
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.0",
- "pkg-dir": "^4.1.0"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- }
- },
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
- "dev": true
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "make-dir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz",
- "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "p-limit": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
- "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "requires": {
- "find-up": "^4.0.0"
- }
- },
- "resolve": {
- "version": "1.12.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
- "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
}
},
- "rollup-pluginutils": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz",
- "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==",
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
"dev": true,
- "requires": {
- "estree-walker": "^0.6.1"
+ "engines": {
+ "node": ">=0.6.19"
}
},
- "rst-selector-parser": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
- "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
- "requires": {
- "lodash.flattendeep": "^4.4.0",
- "nearley": "^2.7.10"
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "run-async": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
- "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "is-promise": "^2.1.0"
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "run-node": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz",
- "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==",
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "run-parallel": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
- "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q=="
- },
- "run-queue": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
- "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+ "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
- "requires": {
- "aproba": "^1.1.1"
+ "engines": {
+ "node": ">=8"
}
},
- "rx-lite": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
- "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
- "dev": true
- },
- "rx-lite-aggregates": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
- "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
- "requires": {
- "rx-lite": "*"
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "rxjs": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
- "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
- "requires": {
- "tslib": "^1.9.0"
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "safe-buffer": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
- "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
- },
- "safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
"dev": true,
- "requires": {
- "ret": "~0.1.10"
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "sax": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
- "dev": true
- },
- "sazerac": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/sazerac/-/sazerac-1.1.0.tgz",
- "integrity": "sha512-UmyjC8/KqnW1mqonl/azoU3KwR+5gMWUnPt78qG2rSG0aZLtzfOSv2zFViZfr7+mjH3nA3UDfWY4vz+EqeHLIw==",
+ "node_modules/string.prototype.padend": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz",
+ "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==",
"dev": true,
- "requires": {
- "deep-eql": "^4.0.0",
- "lodash.at": "^4.6.0",
- "lodash.concat": "^4.5.0",
- "lodash.filter": "^4.6.0",
- "lodash.isfunction": "^3.0.9",
- "lodash.isobject": "^3.0.2",
- "lodash.isstring": "^4.0.1",
- "lodash.isundefined": "^3.0.1",
- "lodash.keys": "^4.2.0",
- "lodash.toarray": "^4.4.0",
- "sprintf-js": "^1.1.2"
- },
"dependencies": {
- "deep-eql": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.0.0.tgz",
- "integrity": "sha512-GxJC5MOg2KyQlv6WiUF/VAnMj4MWnYiXo4oLgeptOELVoknyErb4Z8+5F/IM/K4g9/80YzzatxmWcyRwUseH0A==",
- "dev": true,
- "requires": {
- "type-detect": "^4.0.0"
- }
- },
- "sprintf-js": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
- "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
- "dev": true
- }
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "scheduler": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
- "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
"dev": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
}
},
- "schema-utils": {
- "version": "0.4.7",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
- "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
"dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-keywords": "^3.1.0"
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "scroll-behavior": {
- "version": "0.9.12",
- "resolved": "https://registry.npmjs.org/scroll-behavior/-/scroll-behavior-0.9.12.tgz",
- "integrity": "sha512-18sirtyq1P/VsBX6O/vgw20Np+ngduFXEMO4/NDFXabdOKBL2kjPVUpz1y0+jm99EWwFJafxf5/tCyMeXt9Xyg==",
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
"dev": true,
- "requires": {
- "dom-helpers": "^3.4.0",
- "invariant": "^2.2.4"
- },
"dependencies": {
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- }
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "select-hose": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
- "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo="
- },
- "selfsigned": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
- "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
"dev": true,
- "requires": {
- "node-forge": "0.9.0"
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "semver": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
- "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
- },
- "semver-compare": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
- "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
- "dev": true
- },
- "semver-diff": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
- "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+ "node_modules/stringify-entities": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz",
+ "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==",
"dev": true,
- "requires": {
- "semver": "^5.0.3"
- },
"dependencies": {
- "semver": {
- "version": "5.7.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
- "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
- "dev": true
- }
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "send": {
- "version": "0.17.1",
- "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
- "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "node_modules/stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
"dev": true,
- "requires": {
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "destroy": "~1.0.4",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "~1.7.2",
- "mime": "1.6.0",
- "ms": "2.1.1",
- "on-finished": "~2.3.0",
- "range-parser": "~1.2.1",
- "statuses": "~1.5.0"
- },
+ "license": "BSD-2-Clause",
"dependencies": {
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
- "dev": true
- }
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "serialize-javascript": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
- "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
- "dev": true
- },
- "serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
- "requires": {
- "accepts": "~1.3.4",
- "batch": "0.6.1",
- "debug": "2.6.9",
- "escape-html": "~1.0.3",
- "http-errors": "~1.6.2",
- "mime-types": "~2.1.17",
- "parseurl": "~1.3.2"
- },
"dependencies": {
- "http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
- "dev": true,
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
- }
- },
- "setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
- "dev": true
- }
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "serve-static": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
- "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.17.1"
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
- "dev": true
- },
- "set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
+ "engines": {
+ "node": ">=4"
}
},
- "setimmediate": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
- "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
- "dev": true
- },
- "setprototypeof": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
- "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
- "dev": true
+ "node_modules/strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "sha.js": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
- "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"dev": true,
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
+ "engines": {
+ "node": ">=6"
}
},
- "shallow-compare": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/shallow-compare/-/shallow-compare-1.2.2.tgz",
- "integrity": "sha512-LUMFi+RppPlrHzbqmFnINTrazo0lPNwhcgzuAXVVcfy/mqPDrQmHAyz5bvV0gDAuRFrk804V0HpQ6u9sZ0tBeg==",
- "dev": true
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "shallowequal": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
- "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
- "dev": true
+ "node_modules/striptags": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.2.0.tgz",
+ "integrity": "sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==",
+ "dev": true,
+ "license": "MIT"
},
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "requires": {
- "shebang-regex": "^1.0.0"
+ "node_modules/strnum": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+ "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anynum": "^1.0.1"
}
},
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+ "node_modules/strtok3": {
+ "version": "10.3.5",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz",
+ "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
},
- "shell-quote": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
- "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "node_modules/style-to-object": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz",
+ "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==",
"dev": true,
- "requires": {
- "array-filter": "~0.0.0",
- "array-map": "~0.0.0",
- "array-reduce": "~0.0.0",
- "jsonify": "~0.0.0"
+ "dependencies": {
+ "inline-style-parser": "0.1.1"
}
},
- "side-channel": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz",
- "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==",
- "dev": true,
- "requires": {
- "es-abstract": "^1.17.0-next.1",
- "object-inspect": "^1.7.0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
+ "node_modules/stylehacks": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz",
+ "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-selector-parser": "^6.0.16"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "sift": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/sift/-/sift-5.1.0.tgz",
- "integrity": "sha1-G78t+w63HlbEzH+1Z/vRNRtlAV4=",
- "dev": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
- "dev": true
+ "node_modules/superagent": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.6.tgz",
+ "integrity": "sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==",
+ "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.3",
+ "debug": "^4.3.4",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.0",
+ "formidable": "^2.0.1",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.10.3",
+ "readable-stream": "^3.6.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": ">=6.4.0 <13 || >=14"
+ }
},
- "simple-icons": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-2.9.0.tgz",
- "integrity": "sha512-8ui67F65+ddyWjbo6lqUh2/dHWXkA4q36ms6dra+wWNrClpQoDzfWFyopUBrc3VZ7e1mDJ2boyPVxChlmIS+3Q=="
+ "node_modules/superagent/node_modules/formidable": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz",
+ "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@paralleldrive/cuid2": "^2.2.2",
+ "dezalgo": "^1.0.4",
+ "once": "^1.4.0",
+ "qs": "^6.11.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
},
- "simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "node_modules/superagent/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
"dev": true,
- "requires": {
- "is-arrayish": "^0.3.1"
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
},
- "dependencies": {
- "is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "dev": true
- }
+ "engines": {
+ "node": ">=4.0.0"
}
},
- "sinon": {
+ "node_modules/supports-color": {
"version": "8.1.1",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.1.1.tgz",
- "integrity": "sha512-E+tWr3acRdoe1nXbHMu86SSqA1WGM7Yw3jZRLvlCMnXwTHP8lgFFVn5BnKnF26uc5SfZ3D7pA9sN7S3Y2jG4Ew==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0",
- "@sinonjs/formatio": "^4.0.1",
- "@sinonjs/samsam": "^4.2.2",
- "diff": "^4.0.2",
- "lolex": "^5.1.2",
- "nise": "^3.0.1",
- "supports-color": "^7.1.0"
- },
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
"dependencies": {
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "supports-color": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
- "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "sinon-chai": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz",
- "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==",
- "dev": true
- },
- "sisteransi": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz",
- "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==",
- "dev": true
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "slice-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "node_modules/supports-hyperlinks": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz",
+ "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==",
"dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
- },
+ "license": "MIT",
"dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- }
+ "has-flag": "^5.0.1",
+ "supports-color": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
}
},
- "slugify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz",
- "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ==",
- "dev": true
+ "node_modules/supports-hyperlinks/node_modules/has-flag": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz",
+ "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "snap-shot-compare": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/snap-shot-compare/-/snap-shot-compare-3.0.0.tgz",
- "integrity": "sha512-bdwNOAGuKwPU+qsn0ASxTv+QfkXU+3VmkcDOkt965tes+JQQc8d6SfoLiEiRVhCey4v+ip2IjNUSbZm5nnkI9g==",
+ "node_modules/supports-hyperlinks/node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
"dev": true,
- "requires": {
- "check-more-types": "2.24.0",
- "debug": "4.1.1",
- "disparity": "3.0.0",
- "folktale": "2.3.2",
- "lazy-ass": "1.6.0",
- "strip-ansi": "5.2.0",
- "variable-diff": "1.1.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
},
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "snap-shot-core": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/snap-shot-core/-/snap-shot-core-10.2.0.tgz",
- "integrity": "sha512-FsP+Wd4SCA4bLSm3vi6OVgfmGQcAQkUhwy45zDjZDm/6dZ5SDIgP40ORHg7z6MgMAK2+fj2DmhW7SXyvMU55Vw==",
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
- "requires": {
- "arg": "4.1.0",
- "check-more-types": "2.24.0",
- "common-tags": "1.8.0",
- "debug": "4.1.1",
- "escape-quotes": "1.0.2",
- "folktale": "2.3.2",
- "is-ci": "2.0.0",
- "jsesc": "2.5.2",
- "lazy-ass": "1.6.0",
- "mkdirp": "0.5.1",
- "pluralize": "8.0.0",
- "quote": "0.4.0",
- "ramda": "0.26.1"
+ "engines": {
+ "node": ">= 0.4"
},
- "dependencies": {
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dev": true,
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "snap-shot-it": {
- "version": "7.9.3",
- "resolved": "https://registry.npmjs.org/snap-shot-it/-/snap-shot-it-7.9.3.tgz",
- "integrity": "sha512-S5e59fbbc02AGA94LFWGVl/y/+jLMLr0/aykCtPYBE5rcyV9pSa63Aoik66/L8SkZ9piqqSmBmRAYm46+QvqBA==",
+ "node_modules/svg-parser": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
+ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
"dev": true,
- "requires": {
- "@bahmutov/data-driven": "1.0.0",
- "check-more-types": "2.24.0",
- "common-tags": "1.8.0",
- "debug": "4.1.1",
- "has-only": "1.1.1",
- "its-name": "1.0.0",
- "lazy-ass": "1.6.0",
- "pluralize": "8.0.0",
- "ramda": "0.27.0",
- "snap-shot-compare": "3.0.0",
- "snap-shot-core": "10.2.0"
+ "license": "MIT"
+ },
+ "node_modules/svg-path-bbox": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/svg-path-bbox/-/svg-path-bbox-2.1.0.tgz",
+ "integrity": "sha512-PEoSQFbBvL7FOCE4cN8Knej6L7bXdNkjPcUYsfMMpq0HpnqiO0sE2mcXTd7LX160aOyh5HbaeN/SoY8thMk5Kg==",
+ "dependencies": {
+ "svgpath": "^2.6.0"
+ },
+ "bin": {
+ "svg-path-bbox": "src/cli.js"
},
+ "engines": {
+ "node": ">=6.17.1"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz",
+ "integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "ramda": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz",
- "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==",
- "dev": true
- }
+ "commander": "^7.2.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^2.3.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.0.0",
+ "sax": "^1.5.0"
+ },
+ "bin": {
+ "svgo": "bin/svgo"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
}
},
- "snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "node_modules/svgo/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true,
- "requires": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "dev": true
- }
+ "engines": {
+ "node": ">= 10"
}
},
- "snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "dev": true,
- "requires": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
- "dev": true
- }
+ "node_modules/svgpath": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.6.0.tgz",
+ "integrity": "sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/fontello/svg2ttf?sponsor=1"
}
},
- "snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "node_modules/swagger2openapi": {
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz",
+ "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==",
"dev": true,
- "requires": {
- "kind-of": "^3.2.0"
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "call-me-maybe": "^1.0.1",
+ "node-fetch": "^2.6.1",
+ "node-fetch-h2": "^2.3.0",
+ "node-readfiles": "^0.2.0",
+ "oas-kit-common": "^1.0.8",
+ "oas-resolver": "^2.5.6",
+ "oas-schema-walker": "^1.1.5",
+ "oas-validator": "^5.0.8",
+ "reftools": "^1.1.9",
+ "yaml": "^1.10.0",
+ "yargs": "^17.0.1"
+ },
+ "bin": {
+ "boast": "boast.js",
+ "oas-validate": "oas-validate.js",
+ "swagger2openapi": "swagger2openapi.js"
+ },
+ "funding": {
+ "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.11.13",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz",
+ "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.3.6"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/synckit"
+ }
+ },
+ "node_modules/systeminformation": {
+ "version": "5.31.17",
+ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.17.tgz",
+ "integrity": "sha512-TvFA9iwDWlMjqZVlKIJ0Cy+Zgm9ttlMx0SMRwJDMNKyhlEKWBMb3+WRwDi/3dvHdWbexpos4Osp4U49p5WjB5g==",
+ "dev": true,
+ "license": "MIT",
+ "os": [
+ "darwin",
+ "linux",
+ "win32",
+ "freebsd",
+ "openbsd",
+ "netbsd",
+ "sunos",
+ "android"
+ ],
+ "bin": {
+ "systeminformation": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "Buy me a coffee",
+ "url": "https://www.buymeacoffee.com/systeminfo"
+ }
+ },
+ "node_modules/tagged-tag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "socket.io": {
+ "node_modules/tapable": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
- "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
- "requires": {
- "debug": "~4.1.0",
- "engine.io": "~3.4.0",
- "has-binary2": "~1.0.2",
- "socket.io-adapter": "~1.1.0",
- "socket.io-client": "2.3.0",
- "socket.io-parser": "~3.4.0"
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
},
- "dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
- "socket.io-adapter": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
- "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
- },
- "socket.io-client": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
- "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
- "requires": {
- "backo2": "1.0.2",
- "base64-arraybuffer": "0.1.5",
- "component-bind": "1.0.0",
- "component-emitter": "1.2.1",
- "debug": "~4.1.0",
- "engine.io-client": "~3.4.0",
- "has-binary2": "~1.0.2",
- "has-cors": "1.1.0",
- "indexof": "0.0.1",
- "object-component": "0.0.3",
- "parseqs": "0.0.5",
- "parseuri": "0.0.5",
- "socket.io-parser": "~3.3.0",
- "to-array": "0.1.4"
- },
+ "node_modules/tar": {
+ "version": "7.5.21",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.21.tgz",
+ "integrity": "sha512-XdhtCvlMywwxpCW8YEq3lOXBJpUPTR2OHHcwLPO3HwsJqOHa2Ok/oJ7ruGzp+JrKoRPVCzJwAdEjqLW/vNRPHA==",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "isarray": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "socket.io-parser": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
- "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
- "requires": {
- "component-emitter": "1.2.1",
- "debug": "~3.1.0",
- "isarray": "2.0.1"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- }
- }
- }
- }
- },
- "socket.io-parser": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
- "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
- "requires": {
- "component-emitter": "1.2.1",
- "debug": "~4.1.0",
- "isarray": "2.0.1"
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
},
- "dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "isarray": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
+ "engines": {
+ "node": ">=18"
}
},
- "sockjs": {
- "version": "0.3.19",
- "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
- "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
- "dev": true,
- "requires": {
- "faye-websocket": "^0.10.0",
- "uuid": "^3.0.1"
- },
- "dependencies": {
- "faye-websocket": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
- "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
- "dev": true,
- "requires": {
- "websocket-driver": ">=0.5.1"
- }
- }
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
}
},
- "sockjs-client": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
- "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
- "dev": true,
- "requires": {
- "debug": "^3.2.5",
- "eventsource": "^1.0.7",
- "faye-websocket": "~0.11.1",
- "inherits": "^2.0.3",
- "json3": "^3.3.2",
- "url-parse": "^1.4.3"
- },
+ "node_modules/tdigest": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
+ "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
"dependencies": {
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
+ "bintrees": "1.0.2"
}
},
- "sort-keys": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
- "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=",
+ "node_modules/temp-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz",
+ "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==",
"dev": true,
- "requires": {
- "is-plain-obj": "^1.0.0"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
}
},
- "source-list-map": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
- "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
- "dev": true
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
- },
- "source-map-resolve": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
- "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "node_modules/tempy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.2.0.tgz",
+ "integrity": "sha512-d79HhZya5Djd7am0q+W4RTsSU+D/aJzM+4Y4AGJGuGlgM2L6sx5ZvOYTmZjqPhrDrV6xJTtRSm1JCLj6V6LHLQ==",
"dev": true,
- "requires": {
- "atob": "^2.1.1",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "is-stream": "^3.0.0",
+ "temp-dir": "^3.0.0",
+ "type-fest": "^2.12.2",
+ "unique-string": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "source-map-support": {
- "version": "0.5.12",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
- "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+ "node_modules/tempy/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "source-map-url": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
- "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
- "dev": true
- },
- "spawn-command": {
- "version": "0.0.2-1",
- "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
- "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
- "dev": true
- },
- "spawn-wrap": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
- "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
- "dev": true,
- "requires": {
- "foreground-child": "^2.0.0",
- "is-windows": "^1.0.2",
- "make-dir": "^3.0.0",
- "rimraf": "^3.0.0",
- "signal-exit": "^3.0.2",
- "which": "^2.0.1"
+ "node_modules/terser": {
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.15.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
},
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.16",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
+ "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^4.3.0",
+ "serialize-javascript": "^6.0.2",
+ "terser": "^5.31.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
},
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ "esbuild": {
+ "optional": true
},
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
+ "uglify-js": {
+ "optional": true
}
}
},
- "spdx-correct": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
- "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+ "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
- "requires": {
- "spdx-license-ids": "^1.0.2"
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
}
},
- "spdx-exceptions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
- "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
- "dev": true
- },
- "spdx-expression-parse": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
- "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
- "dev": true
- },
- "spdx-license-ids": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
- "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
- "spdy": {
- "version": "3.4.7",
- "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz",
- "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=",
- "requires": {
- "debug": "^2.6.8",
- "handle-thing": "^1.2.5",
- "http-deceiver": "^1.2.7",
- "safe-buffer": "^5.0.1",
- "select-hose": "^2.0.0",
- "spdy-transport": "^2.0.18"
- },
- "dependencies": {
- "handle-thing": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
- "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ="
- },
- "spdy-transport": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz",
- "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==",
- "requires": {
- "debug": "^2.6.8",
- "detect-node": "^2.0.3",
- "hpack.js": "^2.1.6",
- "obuf": "^1.1.1",
- "readable-stream": "^2.2.9",
- "safe-buffer": "^5.0.1",
- "wbuf": "^1.7.2"
- }
- }
- }
- },
- "spdy-transport": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
- "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "node_modules/test-exclude": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz",
+ "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==",
"dev": true,
- "requires": {
- "debug": "^4.1.0",
- "detect-node": "^2.0.4",
- "hpack.js": "^2.1.6",
- "obuf": "^1.1.2",
- "readable-stream": "^3.0.6",
- "wbuf": "^1.7.3"
- },
+ "license": "ISC",
"dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
- "dev": true
- },
- "readable-stream": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
- "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
- "dev": true,
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
- "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^13.0.6",
+ "minimatch": "^10.2.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
}
},
- "split": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
- "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
+ "node_modules/test-exclude/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
- "requires": {
- "through": "2"
- }
- },
- "split-on-first": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
- "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
- },
- "split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "dev": true,
- "requires": {
- "extend-shallow": "^3.0.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
- },
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ "node_modules/test-exclude/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
},
- "sshpk": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
- "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
- "requires": {
- "asn1": "~0.2.3",
- "assert-plus": "^1.0.0",
- "bcrypt-pbkdf": "^1.0.0",
- "dashdash": "^1.12.0",
- "ecc-jsbn": "~0.1.1",
- "getpass": "^0.1.1",
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.0.2",
- "tweetnacl": "~0.14.0"
+ "node_modules/test-exclude/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "ssri": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
- "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "node_modules/thingies": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz",
+ "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==",
"dev": true,
- "requires": {
- "figgy-pudding": "^3.5.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "^2"
}
},
- "stable": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
- "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "node_modules/throttleit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+ "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==",
"dev": true
},
- "stack-trace": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
- "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
- "dev": true
+ "node_modules/thunky": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "dev": true,
+ "license": "MIT"
},
- "stack-utils": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
- "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==",
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"dev": true
},
- "stackframe": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.1.1.tgz",
- "integrity": "sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ==",
+ "node_modules/tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true
},
- "standard-as-callback": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz",
- "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg=="
- },
- "start-server-and-test": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.10.7.tgz",
- "integrity": "sha512-JEMVYJJSlGPb6w/XlEa0CVOWfAXFvzwKdXhePAJoTlzhSa7AUVntoWFxEh0GGMTMxpgxI7HCejDTdOSkc4aviw==",
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
"dev": true,
- "requires": {
- "bluebird": "3.7.1",
- "check-more-types": "2.24.0",
- "debug": "4.1.1",
- "execa": "2.1.0",
- "lazy-ass": "1.6.0",
- "ps-tree": "1.2.0",
- "wait-on": "4.0.0"
- },
- "dependencies": {
- "bluebird": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
- "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
- "dev": true
- },
- "cross-spawn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
- "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
- "dev": true,
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "execa": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz",
- "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^3.0.0",
- "onetime": "^5.1.0",
- "p-finally": "^2.0.0",
- "signal-exit": "^3.0.2",
- "strip-final-newline": "^2.0.0"
- }
- },
- "get-stream": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
- "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "npm-run-path": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz",
- "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
- "onetime": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
- "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "p-finally": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
- "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
- "dev": true
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
}
},
- "static-eval": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz",
- "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==",
- "requires": {
- "escodegen": "^1.8.1"
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
- "dev": true,
- "requires": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "dev": true,
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "dev": true
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
}
}
},
- "statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
- "dev": true
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
},
- "stealthy-require": {
+ "node_modules/tinypool": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
- "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
},
- "stream-browserify": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
- "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "node_modules/tldts": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
"dev": true,
- "requires": {
- "inherits": "~2.0.1",
- "readable-stream": "^2.0.2"
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^6.1.86"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
}
},
- "stream-combiner": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
- "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
+ "node_modules/tldts-core": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tmp": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
+ "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
"dev": true,
- "requires": {
- "duplexer": "~0.1.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
}
},
- "stream-each": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
- "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+ "node_modules/to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "stream-shift": "^1.0.0"
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
}
},
- "stream-http": {
- "version": "2.8.3",
- "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
- "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "node_modules/to-valid-identifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz",
+ "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==",
"dev": true,
- "requires": {
- "builtin-status-codes": "^3.0.0",
- "inherits": "^2.0.1",
- "readable-stream": "^2.3.6",
- "to-arraybuffer": "^1.0.0",
- "xtend": "^4.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/base62": "^1.0.0",
+ "reserved-identifiers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "stream-shift": {
+ "node_modules/toidentifier": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
- "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
},
- "strict-uri-encode": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
- "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+ "node_modules/token-types": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz",
+ "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@borewit/text-codec": "^0.2.1",
+ "@tokenizer/token": "^0.3.0",
+ "ieee754": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
},
- "string-argv": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
- "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
- "dev": true
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
},
- "string-length": {
+ "node_modules/touch": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz",
- "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
- "optional": true,
- "requires": {
- "astral-regex": "^1.0.0",
- "strip-ansi": "^5.2.0"
- },
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true,
- "optional": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "optional": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "nopt": "~1.0.10"
+ },
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
}
},
- "string-similarity": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-1.2.2.tgz",
- "integrity": "sha512-IoHUjcw3Srl8nsPlW04U3qwWPk3oG2ffLM0tN853d/E/JlIvcmZmDY2Kz5HzKp4lEi2T7QD7Zuvjq/1rDw+XcQ==",
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
- "requires": {
- "lodash.every": "^4.6.0",
- "lodash.flattendeep": "^4.4.0",
- "lodash.foreach": "^4.5.0",
- "lodash.map": "^4.6.0",
- "lodash.maxby": "^4.6.0"
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
}
},
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "node_modules/tree-dump": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz",
+ "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==",
"dev": true,
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.0"
},
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
+ },
+ "peerDependencies": {
+ "tslib": "2"
}
},
- "string.prototype.matchall": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz",
- "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==",
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
- "has-symbols": "^1.0.1",
- "internal-slot": "^1.0.2",
- "regexp.prototype.flags": "^1.3.0",
- "side-channel": "^1.0.2"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- }
+ "bin": {
+ "tree-kill": "cli.js"
}
},
- "string.prototype.padend": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
- "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=",
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "es-abstract": "^1.4.3",
- "function-bind": "^1.0.2"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "string.prototype.trim": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz",
- "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==",
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.0-next.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz",
- "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.4",
- "is-regex": "^1.0.4",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.0",
- "string.prototype.trimright": "^2.1.0"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
- "dev": true
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- }
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "string.prototype.trimend": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz",
- "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==",
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
},
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
- "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
- "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimstart": "^1.0.0"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
- "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimend": "^1.0.0"
- }
- }
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
}
},
- "string.prototype.trimleft": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
- "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+ "node_modules/ts-declaration-location": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz",
+ "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
+ "funding": [
+ {
+ "type": "ko-fi",
+ "url": "https://ko-fi.com/rebeccastevens"
},
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/ts-declaration-location"
}
+ ],
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "picomatch": "^4.0.2"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.0.0"
}
},
- "string.prototype.trimright": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
- "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+ "node_modules/ts-declaration-location/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
},
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- }
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "string.prototype.trimstart": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz",
- "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==",
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5"
- },
"dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "es-abstract": {
- "version": "1.17.5",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
- "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-symbol": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
- "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
- "dev": true,
- "requires": {
- "has-symbols": "^1.0.1"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true
- },
- "string.prototype.trimleft": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
- "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimstart": "^1.0.0"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
- "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.5",
- "string.prototype.trimend": "^1.0.0"
- }
- }
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
}
},
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
}
},
- "stringify-object": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
- "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "node_modules/tsd": {
+ "version": "0.33.0",
+ "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.33.0.tgz",
+ "integrity": "sha512-/PQtykJFVw90QICG7zyPDMIyueOXKL7jOJVoX5pILnb3Ux+7QqynOxfVvarE+K+yi7BZyOSY4r+OZNWSWRiEwQ==",
"dev": true,
- "requires": {
- "get-own-enumerable-property-symbols": "^3.0.0",
- "is-obj": "^1.0.1",
- "is-regexp": "^1.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@tsd/typescript": "^5.9.2",
+ "eslint-formatter-pretty": "^4.1.0",
+ "globby": "^11.0.1",
+ "jest-diff": "^29.0.3",
+ "meow": "^9.0.0",
+ "path-exists": "^4.0.0",
+ "read-pkg-up": "^7.0.0"
+ },
+ "bin": {
+ "tsd": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
}
},
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "node_modules/tsd/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
+ "engines": {
+ "node": ">=6"
}
},
- "strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
- "dev": true
- },
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
- "dev": true
- },
- "strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true
- },
- "strip-indent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
- "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
- "dev": true
+ "node_modules/tsd/node_modules/camelcase-keys": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+ "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "map-obj": "^4.0.0",
+ "quick-lru": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
- "dev": true
+ "node_modules/tsd/node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "style-loader": {
- "version": "0.23.1",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
- "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
+ "node_modules/tsd/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "schema-utils": "^1.0.0"
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tsd/node_modules/hosted-git-info": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+ "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+ "dev": true,
"dependencies": {
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- }
+ "lru-cache": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "style-value-types": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-3.1.7.tgz",
- "integrity": "sha512-jPaG5HcAPs3vetSwOJozrBXxuHo9tjZVnbRyBjxqb00c2saIoeuBJc1/2MtvB8eRZy41u/BBDH0CpfzWixftKg==",
+ "node_modules/tsd/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
- "requires": {
- "hey-listen": "^1.0.8",
- "tslib": "^1.10.0"
+ "dependencies": {
+ "p-locate": "^4.1.0"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tsd/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
"dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "styled-components": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.0.tgz",
- "integrity": "sha512-0Qs2wEkFBXHFlysz6CV831VG6HedcrFUwChjnWylNivsx14MtmqQsohi21rMHZxzuTba063dEyoe/SR6VGJI7Q==",
- "dev": true,
- "requires": {
- "@babel/helper-module-imports": "^7.0.0",
- "@babel/traverse": "^7.4.5",
- "@emotion/is-prop-valid": "^0.8.8",
- "@emotion/stylis": "^0.8.4",
- "@emotion/unitless": "^0.7.4",
- "babel-plugin-styled-components": ">= 1",
- "css-to-react-native": "^3.0.0",
- "hoist-non-react-statics": "^3.0.0",
- "shallowequal": "^1.1.0",
- "supports-color": "^5.5.0"
- },
- "dependencies": {
- "@emotion/is-prop-valid": {
- "version": "0.8.8",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
- "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
- "dev": true,
- "requires": {
- "@emotion/memoize": "0.7.4"
- }
- },
- "@emotion/memoize": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
- "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
+ "node_modules/tsd/node_modules/meow": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
+ "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimist": "^1.2.0",
+ "camelcase-keys": "^6.2.2",
+ "decamelize": "^1.2.0",
+ "decamelize-keys": "^1.1.0",
+ "hard-rejection": "^2.1.0",
+ "minimist-options": "4.1.0",
+ "normalize-package-data": "^3.0.0",
+ "read-pkg-up": "^7.0.1",
+ "redent": "^3.0.0",
+ "trim-newlines": "^3.0.0",
+ "type-fest": "^0.18.0",
+ "yargs-parser": "^20.2.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "stylefire": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/stylefire/-/stylefire-7.0.1.tgz",
- "integrity": "sha512-xp7hGiK5xyX7xINQI7qWagcLMyS6aLLbP2kMA3DTS4X31uPI3M7IyOm9TPmXwaMKG9fJ2Cgo4F2okcNB6nzxKQ==",
+ "node_modules/tsd/node_modules/normalize-package-data": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
+ "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
"dev": true,
- "requires": {
- "@popmotion/popcorn": "^0.4.4",
- "framesync": "^4.0.0",
- "hey-listen": "^1.0.8",
- "style-value-types": "^3.1.7",
- "tslib": "^1.10.0"
+ "dependencies": {
+ "hosted-git-info": "^4.0.1",
+ "is-core-module": "^2.5.0",
+ "semver": "^7.3.4",
+ "validate-npm-package-license": "^3.0.1"
},
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tsd/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
"dependencies": {
- "tslib": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
- "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
- "dev": true
- }
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "stylehacks": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
- "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
+ "node_modules/tsd/node_modules/quick-lru": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
+ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true,
- "requires": {
- "browserslist": "^4.0.0",
- "postcss": "^7.0.0",
- "postcss-selector-parser": "^3.0.0"
- },
- "dependencies": {
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "postcss-selector-parser": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
- "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
- }
- }
- }
- },
- "sudo-prompt": {
- "version": "8.2.5",
- "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz",
- "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==",
- "dev": true
+ "engines": {
+ "node": ">=8"
+ }
},
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "node_modules/tsd/node_modules/read-pkg": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+ "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
"dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- },
"dependencies": {
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- }
+ "@types/normalize-package-data": "^2.4.0",
+ "normalize-package-data": "^2.5.0",
+ "parse-json": "^5.0.0",
+ "type-fest": "^0.6.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "supports-hyperlinks": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz",
- "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==",
+ "node_modules/tsd/node_modules/read-pkg-up": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+ "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
"dev": true,
- "requires": {
- "has-flag": "^2.0.0",
- "supports-color": "^5.0.0"
+ "dependencies": {
+ "find-up": "^4.1.0",
+ "read-pkg": "^5.2.0",
+ "type-fest": "^0.8.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "svgo": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.1.1.tgz",
- "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==",
- "dev": true,
- "requires": {
- "coa": "~2.0.1",
- "colors": "~1.1.2",
- "css-select": "^2.0.0",
- "css-select-base-adapter": "~0.1.0",
- "css-tree": "1.0.0-alpha.28",
- "css-url-regex": "^1.1.0",
- "csso": "^3.5.0",
- "js-yaml": "^3.12.0",
- "mkdirp": "~0.5.1",
- "object.values": "^1.0.4",
- "sax": "~1.2.4",
- "stable": "~0.1.6",
- "unquote": "~1.1.1",
- "util.promisify": "~1.0.0"
- },
- "dependencies": {
- "css-select": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.0.tgz",
- "integrity": "sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "2.1",
- "domutils": "^1.7.0",
- "nth-check": "^1.0.1"
- }
- },
- "domutils": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
- "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
- "dev": true,
- "requires": {
- "dom-serializer": "0",
- "domelementtype": "1"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- }
+ "node_modules/tsd/node_modules/read-pkg-up/node_modules/type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
}
},
- "symbol-observable": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
- "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "node_modules/tsd/node_modules/read-pkg/node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
- "table": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
- "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
+ "node_modules/tsd/node_modules/read-pkg/node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
- "requires": {
- "ajv": "^6.9.1",
- "lodash": "^4.17.11",
- "slice-ansi": "^2.1.0",
- "string-width": "^3.0.0"
- },
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
}
},
- "taffydb": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
- "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=",
- "dev": true
- },
- "tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "dev": true
- },
- "tdigest": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
- "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=",
- "requires": {
- "bintrees": "1.0.1"
+ "node_modules/tsd/node_modules/read-pkg/node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
}
},
- "temp-dir": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
- "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
- "dev": true
+ "node_modules/tsd/node_modules/read-pkg/node_modules/type-fest": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+ "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "temp-write": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz",
- "integrity": "sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw==",
+ "node_modules/tsd/node_modules/redent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.15",
- "is-stream": "^2.0.0",
- "make-dir": "^3.0.0",
- "temp-dir": "^1.0.0",
- "uuid": "^3.3.2"
- },
"dependencies": {
- "graceful-fs": {
- "version": "4.1.15",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
- "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
- "dev": true
- },
- "is-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
- "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
- "dev": true
- },
- "make-dir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz",
- "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "uuid": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
- "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
- "dev": true
- }
+ "indent-string": "^4.0.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "term-size": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
- "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
- "dev": true,
- "requires": {
- "execa": "^0.7.0"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
- "requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "execa": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
- "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
- "dev": true,
- "requires": {
- "cross-spawn": "^5.0.1",
- "get-stream": "^3.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- }
- }
- }
- },
- "terser": {
- "version": "4.6.6",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
- "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
- "dev": true,
- "requires": {
- "commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
- },
+ "node_modules/tsd/node_modules/strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
"dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "terser-webpack-plugin": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
- "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
- "dev": true,
- "requires": {
- "cacache": "^12.0.2",
- "find-cache-dir": "^2.1.0",
- "is-wsl": "^1.1.0",
- "schema-utils": "^1.0.0",
- "serialize-javascript": "^2.1.2",
- "source-map": "^0.6.1",
- "terser": "^4.1.2",
- "webpack-sources": "^1.4.0",
- "worker-farm": "^1.7.0"
- },
- "dependencies": {
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "node_modules/tsd/node_modules/trim-newlines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
+ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true,
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
+ "engines": {
+ "node": ">=8"
}
},
- "text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
- "dev": true
- },
- "throttleit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
- "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
- "dev": true
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
- "dev": true
- },
- "through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "node_modules/tsd/node_modules/type-fest": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
+ "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
"dev": true,
- "requires": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "thunky": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
- "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "node_modules/tsd/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
- "timed-out": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
- "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
- "dev": true
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
- "timers-browserify": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
- "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+ "node_modules/tsyringe": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
+ "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
"dev": true,
- "requires": {
- "setimmediate": "^1.0.4"
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
}
},
- "timsort": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
- "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
- "dev": true
+ "node_modules/tsyringe/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true,
+ "license": "0BSD"
},
- "tmp": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
- "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
- "requires": {
- "rimraf": "^2.6.3"
- },
"dependencies": {
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
}
},
- "to-array": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
- "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
- },
- "to-arraybuffer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
- "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
- "dev": true
- },
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true
},
- "to-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
- "requires": {
- "kind-of": "^3.0.2"
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
- "to-readable-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
- "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
- "dev": true
- },
- "to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "dev": true,
- "requires": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
- "dev": true,
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
- },
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- }
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
}
},
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
+ "engines": {
+ "node": ">=12.20"
},
- "dependencies": {
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- }
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "toidentifier": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
- "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
- "dev": true
- },
- "touch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
- "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
- "requires": {
- "nopt": "~1.0.10"
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "tough-cookie": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
- "requires": {
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
- },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
"dependencies": {
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
- }
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
"dev": true,
- "requires": {
- "punycode": "^2.1.0"
- },
"dependencies": {
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
- "dev": true
- }
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "tree-kill": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
- "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
- "dev": true
- },
- "trim-newlines": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz",
- "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=",
- "dev": true
- },
- "true-case-path": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz",
- "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==",
- "dev": true
- },
- "ts-essentials": {
+ "node_modules/typed-array-byte-offset": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-1.0.4.tgz",
- "integrity": "sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "ts-mocha": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-7.0.0.tgz",
- "integrity": "sha512-7WfkQw1W6JZXG5m4E1w2e945uWzBoZqmnOHvpMu0v+zvyKLdUQeTtRMfcQsVEKsUnYL6nTyH4okRt2PZucmFXQ==",
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
"dev": true,
- "requires": {
- "ts-node": "7.0.1",
- "tsconfig-paths": "^3.5.0"
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "ts-node": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
- "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
- "dev": true,
- "requires": {
- "arrify": "^1.0.0",
- "buffer-from": "^1.1.0",
- "diff": "^3.1.0",
- "make-error": "^1.1.1",
- "minimist": "^1.2.0",
- "mkdirp": "^0.5.1",
- "source-map-support": "^0.5.6",
- "yn": "^2.0.0"
+ "node_modules/typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
}
},
- "ts-pnp": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz",
- "integrity": "sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ==",
- "dev": true
+ "node_modules/typescript": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
+ "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
},
- "tsconfig-paths": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
- "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "node_modules/typescript-eslint": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
+ "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
"dev": true,
- "optional": true,
- "requires": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.1",
- "minimist": "^1.2.0",
- "strip-bom": "^3.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.58.0",
+ "@typescript-eslint/parser": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "tslib": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
- "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
- },
- "tsutils": {
- "version": "3.17.1",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
- "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
+ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
"dev": true,
- "requires": {
- "tslib": "^1.8.1"
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
- "tty-browserify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
- "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
- "dev": true
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
- "requires": {
- "safe-buffer": "^5.0.1"
+ "node_modules/typescript-eslint/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "tweetnacl": {
- "version": "0.14.5",
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+ "node_modules/typescript-eslint/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
},
- "type-check": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
- "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
- "requires": {
- "prelude-ls": "~1.1.2"
+ "node_modules/uc-first-array": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/uc-first-array/-/uc-first-array-1.1.10.tgz",
+ "integrity": "sha512-tX2PJLrqtexTxVN9hTTY+K5gPnF2gyj7SfjPF4Q2Xhbi1fSNiO12I/G+AoMzxJLwr9R50CmVn8iAhWCvZlJm3A==",
+ "dependencies": {
+ "ucfirst": "^1.0.0"
}
},
- "type-detect": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz",
- "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo="
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "dev": true,
+ "license": "MIT"
},
- "type-fest": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
- "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
- "dev": true
+ "node_modules/ucfirst": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ucfirst/-/ucfirst-1.0.0.tgz",
+ "integrity": "sha512-xbB/CQ0GdkxqH4IElZqenn/dL/tnyx7DCDASWJPE92ePbFM21kKemXI2LBeYtEvblf1Ol98hyJJS43Wu5JMQSQ=="
},
- "type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "dev": true,
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
+ "node_modules/uint8array-extras": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
+ "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
},
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
"dependencies": {
- "mime-db": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
- "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.24",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
- "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
- "dev": true,
- "requires": {
- "mime-db": "1.40.0"
- }
- }
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "type-of": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/type-of/-/type-of-2.0.1.tgz",
- "integrity": "sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=",
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
- "typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "node_modules/underscore": {
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
+ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
- "typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
- "dev": true,
- "requires": {
- "is-typedarray": "^1.0.0"
+ "node_modules/undici": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz",
+ "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
}
},
- "typescript": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
- "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
- "dev": true
- },
- "uc-first-array": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/uc-first-array/-/uc-first-array-1.1.9.tgz",
- "integrity": "sha512-RHBUloM1Onz7ZKplDU0FWIXWHxU8Ylztw/ORsFY/py6If5B+8PWd39j9ll+i8jUaAAs2Rs2lKMbxvm4u8Q2dbg==",
- "requires": {
- "ucfirst": "^1.0.0"
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
}
},
- "uc.micro": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
- "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
- "dev": true
- },
- "ucfirst": {
+ "node_modules/unicode-emoji-modifier-base": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/ucfirst/-/ucfirst-1.0.0.tgz",
- "integrity": "sha1-ThBbZEjQXiZOzsQ14LkZNjxfLy8="
- },
- "un-eval": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/un-eval/-/un-eval-1.2.0.tgz",
- "integrity": "sha512-Wlj/pum6dQtGTPD/lclDtoVPkSfpjPfy1dwnnKw/sZP5DpBH9fLhBgQfsqNhe5/gS1D+vkZUuB771NRMUPA5CA==",
- "dev": true
+ "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
+ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
},
- "unc-path-regex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
- "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
- "dev": true
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
- "undefsafe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
- "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==",
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
"dev": true,
- "requires": {
- "debug": "^2.2.0"
+ "engines": {
+ "node": ">=4"
}
},
- "underscore": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
- "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk="
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
},
- "unicode-canonical-property-names-ecmascript": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
- "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
- "dev": true
+ "node_modules/unicorn-magic": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+ "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "unicode-match-property-ecmascript": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
- "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "node_modules/unified": {
+ "version": "11.0.4",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz",
+ "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==",
"dev": true,
- "requires": {
- "unicode-canonical-property-names-ecmascript": "^1.0.4",
- "unicode-property-aliases-ecmascript": "^1.0.4"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "unicode-match-property-value-ecmascript": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz",
- "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==",
- "dev": true
+ "node_modules/unified/node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "unicode-property-aliases-ecmascript": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz",
- "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==",
- "dev": true
+ "node_modules/unique-string": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
+ "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
+ "dev": true,
+ "dependencies": {
+ "crypto-random-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"dev": true,
- "requires": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "uniq": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
- "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
- "dev": true
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "uniqs": {
+ "node_modules/unist-util-position-from-estree": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
- "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
- "dev": true
- },
- "unique-filename": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
- "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
"dev": true,
- "requires": {
- "unique-slug": "^2.0.0"
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "unique-slug": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
- "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "node_modules/unist-util-remove-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz",
+ "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==",
"dev": true,
- "requires": {
- "imurmurhash": "^0.1.4"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "unique-string": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
- "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"dev": true,
- "requires": {
- "crypto-random-string": "^1.0.0"
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "universal-url": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universal-url/-/universal-url-2.0.0.tgz",
- "integrity": "sha512-3DLtXdm/G1LQMCnPj+Aw7uDoleQttNHp2g5FnNQKR6cP6taNWS1b/Ehjjx4PVyvejKi3TJyu8iBraKM4q3JQPg==",
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
"dev": true,
- "requires": {
- "hasurl": "^1.0.0",
- "whatwg-url": "^7.0.0"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "universal-user-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz",
- "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==",
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"dev": true,
- "requires": {
- "os-name": "^3.1.0"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "dev": true
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
- "dev": true
+ "node_modules/universal-user-agent": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+ "dev": true,
+ "license": "ISC"
},
- "unquote": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
- "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
- "dev": true
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
},
- "unset-value": {
+ "node_modules/unpipe": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
- "dev": true,
- "requires": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
- "dev": true,
- "requires": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
- "dev": true,
- "requires": {
- "isarray": "1.0.0"
- }
- }
- }
- },
- "has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
- "dev": true
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- }
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "untildify": {
+ "node_modules/untildify": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
- "dev": true
- },
- "upath": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
- "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "update-notifier": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz",
- "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==",
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
- "requires": {
- "boxen": "^4.2.0",
- "chalk": "^3.0.0",
- "configstore": "^5.0.1",
- "has-yarn": "^2.1.0",
- "import-lazy": "^2.1.0",
- "is-ci": "^2.0.0",
- "is-installed-globally": "^0.3.1",
- "is-npm": "^4.0.0",
- "is-yarn-global": "^0.3.0",
- "latest-version": "^5.0.0",
- "pupa": "^2.0.1",
- "semver-diff": "^3.1.1",
- "xdg-basedir": "^4.0.0"
- },
- "dependencies": {
- "configstore": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
- "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
- "dev": true,
- "requires": {
- "dot-prop": "^5.2.0",
- "graceful-fs": "^4.1.2",
- "make-dir": "^3.0.0",
- "unique-string": "^2.0.0",
- "write-file-atomic": "^3.0.0",
- "xdg-basedir": "^4.0.0"
- }
- },
- "crypto-random-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
- "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
- "dev": true
- },
- "dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
- "dev": true,
- "requires": {
- "is-obj": "^2.0.0"
- }
- },
- "global-dirs": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz",
- "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==",
- "dev": true,
- "requires": {
- "ini": "^1.3.5"
- }
- },
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
- "dev": true
- },
- "is-installed-globally": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz",
- "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==",
- "dev": true,
- "requires": {
- "global-dirs": "^2.0.1",
- "is-path-inside": "^3.0.1"
- }
- },
- "is-obj": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
- },
- "is-path-inside": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz",
- "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==",
- "dev": true
- },
- "make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- }
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
},
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
},
- "semver-diff": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
- "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
- "dev": true,
- "requires": {
- "semver": "^6.3.0"
- }
- },
- "unique-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
- "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
- "dev": true,
- "requires": {
- "crypto-random-string": "^2.0.0"
- }
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "dev": true,
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "xdg-basedir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
- "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
- "dev": true
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
}
},
- "uri-js": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
- "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
- "requires": {
- "punycode": "^2.1.0"
- },
+ "node_modules/update-notifier": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz",
+ "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boxen": "^7.0.0",
+ "chalk": "^5.0.1",
+ "configstore": "^6.0.0",
+ "has-yarn": "^3.0.0",
+ "import-lazy": "^4.0.0",
+ "is-ci": "^3.0.1",
+ "is-installed-globally": "^0.4.0",
+ "is-npm": "^6.0.0",
+ "is-yarn-global": "^0.4.0",
+ "latest-version": "^7.0.0",
+ "pupa": "^3.1.0",
+ "semver": "^7.3.7",
+ "semver-diff": "^4.0.0",
+ "xdg-basedir": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/yeoman/update-notifier?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/boxen": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz",
+ "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
- }
+ "ansi-align": "^3.0.1",
+ "camelcase": "^7.0.1",
+ "chalk": "^5.2.0",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^5.1.2",
+ "type-fest": "^2.13.0",
+ "widest-line": "^4.0.1",
+ "wrap-ansi": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
- "dev": true
+ "node_modules/update-notifier/node_modules/camelcase": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
+ "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "url": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz",
+ "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==",
"dev": true,
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.12.3"
},
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/url-loader": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
+ "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
+ "dev": true,
"dependencies": {
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
- "dev": true
+ "loader-utils": "^2.0.0",
+ "mime-types": "^2.1.27",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "file-loader": "*",
+ "webpack": "^4.0.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "file-loader": {
+ "optional": true
}
}
},
- "url-loader": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz",
- "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==",
+ "node_modules/url-loader/node_modules/schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "mime": "^2.0.3",
- "schema-utils": "^1.0.0"
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
},
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "dev": true
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
- "dev": true
- },
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- }
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/util/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/utility-types": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
+ "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
}
},
- "url-parse": {
- "version": "1.4.7",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
- "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+ "node_modules/v8-to-istanbul": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+ "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
"dev": true,
- "requires": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.12",
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=10.12.0"
}
},
- "url-parse-lax": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
- "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
- "requires": {
- "prepend-http": "^2.0.0"
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
}
},
- "url-to-options": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
- "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=",
- "dev": true
+ "node_modules/validate.io-array": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz",
+ "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==",
+ "dev": true,
+ "license": "MIT"
},
- "use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "node_modules/validate.io-function": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz",
+ "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==",
"dev": true
},
- "util": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
- "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "node_modules/validate.io-integer": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz",
+ "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==",
"dev": true,
- "requires": {
- "inherits": "2.0.3"
+ "dependencies": {
+ "validate.io-number": "^1.0.3"
}
},
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
- },
- "util.promisify": {
+ "node_modules/validate.io-integer-array": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
- "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+ "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz",
+ "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==",
"dev": true,
- "requires": {
- "define-properties": "^1.1.2",
- "object.getownpropertydescriptors": "^2.0.3"
+ "dependencies": {
+ "validate.io-array": "^1.0.3",
+ "validate.io-integer": "^1.0.4"
}
},
- "utila": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
- "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
+ "node_modules/validate.io-number": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz",
+ "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==",
"dev": true
},
- "utils-merge": {
+ "node_modules/value-equal": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
+ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==",
"dev": true
},
- "uuid": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
- "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+ "node_modules/variable-diff": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/variable-diff/-/variable-diff-1.1.0.tgz",
+ "integrity": "sha512-0Jk/MsCNtL/fCuVIbsLxwXpABGZCzN57btcPbSsjOOAwkdHJ3Y58fo8BoUfG7jghnvglbwo+5Hk1KOJ2W2Ormw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.1",
+ "object-assign": "^4.0.1"
+ }
},
- "v8-compile-cache": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz",
- "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==",
- "dev": true
+ "node_modules/variable-diff/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "validate-npm-package-license": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
- "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+ "node_modules/variable-diff/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
"dev": true,
- "requires": {
- "spdx-correct": "~1.0.0",
- "spdx-expression-parse": "~1.0.0"
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "variable-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/variable-diff/-/variable-diff-1.1.0.tgz",
- "integrity": "sha1-0r1cZtt2wTh52W5qMG7cmJ35eNo=",
+ "node_modules/variable-diff/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"dev": true,
- "requires": {
- "chalk": "^1.1.1",
- "object-assign": "^4.0.1"
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
},
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/variable-diff/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/variable-diff/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
"dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- }
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "vary": {
+ "node_modules/variable-diff/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
- "dev": true
- },
- "vendors": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz",
- "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==",
- "dev": true
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
},
- "verror": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
- "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
- "requires": {
+ "node_modules/verror": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
+ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+ "dev": true,
+ "dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
},
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
+ "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
+ "dev": true,
"dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
- }
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "vm-browserify": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
- "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
- "dev": true
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "wait-on": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-4.0.0.tgz",
- "integrity": "sha512-QrW3J8LzS5ADPfD9Rx5S6KJck66xkqyiFKQs9jmUTkIhiEOmkzU7WRZc+MjsnmkrgjitS2xQ4bb13hnlQnKBUQ==",
+ "node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"dev": true,
- "requires": {
- "@hapi/joi": "^16.1.8",
- "lodash": "^4.17.15",
- "minimist": "^1.2.0",
- "request": "^2.88.0",
- "request-promise-native": "^1.0.8",
- "rxjs": "^6.5.4"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
},
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/wait-on": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.10.tgz",
+ "integrity": "sha512-rCoJEhvMr0X6alHmwc9abbrA5ZrLZFKpFQVKPNFwl2h7DapXOGdmimIHDtLOWhT4PjhZhxFEtZoQgEXbkDWdZw==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "rxjs": {
- "version": "6.5.4",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
- "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
- "dev": true,
- "requires": {
- "tslib": "^1.9.0"
- }
- }
+ "axios": "^1.16.0",
+ "joi": "^18.2.1",
+ "lodash": "^4.18.1",
+ "minimist": "^1.2.8",
+ "rxjs": "^7.8.2"
+ },
+ "bin": {
+ "wait-on": "bin/wait-on"
+ },
+ "engines": {
+ "node": ">=20.0.0"
}
},
- "wait-promise": {
+ "node_modules/wait-on/node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wait-promise": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/wait-promise/-/wait-promise-0.4.1.tgz",
- "integrity": "sha1-Hq2PgtKRolAQwLHu+NRxEyPMTas=",
+ "integrity": "sha512-qr38zpL1yTDNbRzuBNEGOp3O6+5H3j5pUhK6lGo88gOHooP++Tk8eT4v+8/mhMPjICrY5/zOJJO+bDT3i0k/qA==",
"dev": true
},
- "warning": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
- "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "watchpack": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
- "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
+ "node_modules/watchpack": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
+ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
"dev": true,
- "requires": {
- "chokidar": "^2.0.2",
- "graceful-fs": "^4.1.2",
- "neo-async": "^2.5.0"
- },
- "dependencies": {
- "chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "dev": true,
- "requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "fsevents": "^1.2.7",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- }
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- }
+ "license": "MIT",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
}
},
- "wbuf": {
+ "node_modules/wbuf": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
"integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
- "requires": {
+ "dependencies": {
"minimalistic-assert": "^1.0.0"
}
},
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
- "dev": true
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/webextension-store-meta": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/webextension-store-meta/-/webextension-store-meta-1.3.0.tgz",
+ "integrity": "sha512-VQc5Wyvrndh+GmI20DUcVXWNfPH3f2t+XwXLGMnv/lgH6bdmFoahG1o3w55sr2Ct7+ngbOxMkSFdGRmG46UciA==",
+ "license": "MIT",
+ "dependencies": {
+ "domhandler": "^4.0.0",
+ "htmlparser2": "^6.1.0",
+ "pretty-bytes": "^7.1.0",
+ "undici": "^6.11.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webextension-store-meta/node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/webextension-store-meta/node_modules/domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/webextension-store-meta/node_modules/domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/webextension-store-meta/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/webextension-store-meta/node_modules/htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
+ }
},
- "webpack": {
- "version": "4.41.6",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz",
- "integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==",
- "dev": true,
- "requires": {
- "@webassemblyjs/ast": "1.8.5",
- "@webassemblyjs/helper-module-context": "1.8.5",
- "@webassemblyjs/wasm-edit": "1.8.5",
- "@webassemblyjs/wasm-parser": "1.8.5",
- "acorn": "^6.2.1",
- "ajv": "^6.10.2",
- "ajv-keywords": "^3.4.1",
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "node_modules/webpack": {
+ "version": "5.105.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
+ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.7",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
+ "@webassemblyjs/ast": "^1.14.1",
+ "@webassemblyjs/wasm-edit": "^1.14.1",
+ "@webassemblyjs/wasm-parser": "^1.14.1",
+ "acorn": "^8.15.0",
+ "acorn-import-phases": "^1.0.3",
+ "browserslist": "^4.28.1",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^4.1.0",
- "eslint-scope": "^4.0.3",
- "json-parse-better-errors": "^1.0.2",
- "loader-runner": "^2.4.0",
- "loader-utils": "^1.2.3",
- "memory-fs": "^0.4.1",
- "micromatch": "^3.1.10",
- "mkdirp": "^0.5.1",
- "neo-async": "^2.6.1",
- "node-libs-browser": "^2.2.1",
- "schema-utils": "^1.0.0",
- "tapable": "^1.1.3",
- "terser-webpack-plugin": "^1.4.3",
- "watchpack": "^1.6.0",
- "webpack-sources": "^1.4.1"
- },
- "dependencies": {
- "acorn": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
- "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
- "dev": true
- },
- "ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
- "dev": true,
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- }
- },
- "eslint-scope": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
- "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
- "dev": true,
- "requires": {
- "esrecurse": "^4.1.0",
- "estraverse": "^4.1.1"
- }
+ "enhanced-resolve": "^5.19.0",
+ "es-module-lexer": "^2.0.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.11",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.3.1",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^4.3.3",
+ "tapable": "^2.3.0",
+ "terser-webpack-plugin": "^5.3.16",
+ "watchpack": "^2.5.1",
+ "webpack-sources": "^3.3.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-bundle-analyzer": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
+ "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@discoveryjs/json-ext": "0.5.7",
+ "acorn": "^8.0.4",
+ "acorn-walk": "^8.0.0",
+ "commander": "^7.2.0",
+ "debounce": "^1.2.1",
+ "escape-string-regexp": "^4.0.0",
+ "gzip-size": "^6.0.0",
+ "html-escaper": "^2.0.2",
+ "opener": "^1.5.2",
+ "picocolors": "^1.0.0",
+ "sirv": "^2.0.3",
+ "ws": "^7.3.1"
+ },
+ "bin": {
+ "webpack-bundle-analyzer": "lib/bin/analyzer.js"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/ws": {
+ "version": "7.5.12",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.12.tgz",
+ "integrity": "sha512-1xGnbYN3zbog9CwuNDQULNRrTCLIn46/WmpR1f0w6PsCYQHkylZr5vkd6kfMZYV6pRnQkcPNRyiA8LsrNKyhpg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
},
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
+ "utf-8-validate": {
+ "optional": true
}
}
},
- "webpack-dev-middleware": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
- "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==",
+ "node_modules/webpack-dev-middleware": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz",
+ "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==",
"dev": true,
- "requires": {
- "memory-fs": "^0.4.1",
- "mime": "^2.4.4",
- "mkdirp": "^0.5.1",
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "^2.0.10",
+ "memfs": "^4.43.1",
+ "mime-types": "^3.0.1",
+ "on-finished": "^2.4.1",
"range-parser": "^1.2.1",
- "webpack-log": "^2.0.0"
+ "schema-utils": "^4.0.0"
},
- "dependencies": {
- "mime": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
- "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
- "dev": true
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack": {
+ "optional": true
}
}
},
- "webpack-dev-server": {
- "version": "3.10.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz",
- "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==",
+ "node_modules/webpack-dev-middleware/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"dev": true,
- "requires": {
- "ansi-html": "0.0.7",
- "bonjour": "^3.5.0",
- "chokidar": "^2.1.8",
- "compression": "^1.7.4",
- "connect-history-api-fallback": "^1.6.0",
- "debug": "^4.1.1",
- "del": "^4.1.1",
- "express": "^4.17.1",
- "html-entities": "^1.2.1",
- "http-proxy-middleware": "0.19.1",
- "import-local": "^2.0.0",
- "internal-ip": "^4.3.0",
- "ip": "^1.1.5",
- "is-absolute-url": "^3.0.3",
- "killable": "^1.0.1",
- "loglevel": "^1.6.6",
- "opn": "^5.5.0",
- "p-retry": "^3.0.1",
- "portfinder": "^1.0.25",
- "schema-utils": "^1.0.0",
- "selfsigned": "^1.10.7",
- "semver": "^6.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/webpack-dev-middleware/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/webpack-dev-server": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.6.tgz",
+ "integrity": "sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/bonjour": "^3.5.13",
+ "@types/connect-history-api-fallback": "^1.5.4",
+ "@types/express": "^4.17.25",
+ "@types/express-serve-static-core": "^4.17.21",
+ "@types/serve-index": "^1.9.4",
+ "@types/serve-static": "^1.15.5",
+ "@types/sockjs": "^0.3.36",
+ "@types/ws": "^8.5.10",
+ "ansi-html-community": "^0.0.8",
+ "bonjour-service": "^1.2.1",
+ "chokidar": "^3.6.0",
+ "colorette": "^2.0.10",
+ "compression": "^1.8.1",
+ "connect-history-api-fallback": "^2.0.0",
+ "express": "^4.22.1",
+ "graceful-fs": "^4.2.6",
+ "http-proxy-middleware": "^2.0.9",
+ "ipaddr.js": "^2.1.0",
+ "launch-editor": "^2.14.1",
+ "open": "^10.0.3",
+ "p-retry": "^6.2.0",
+ "schema-utils": "^4.2.0",
+ "selfsigned": "^5.5.0",
"serve-index": "^1.9.1",
- "sockjs": "0.3.19",
- "sockjs-client": "1.4.0",
- "spdy": "^4.0.1",
- "strip-ansi": "^3.0.1",
- "supports-color": "^6.1.0",
- "url": "^0.11.0",
- "webpack-dev-middleware": "^3.7.2",
- "webpack-log": "^2.0.0",
- "ws": "^6.2.1",
- "yargs": "12.0.5"
- },
- "dependencies": {
- "array-union": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
- "dev": true,
- "requires": {
- "array-uniq": "^1.0.1"
- }
- },
- "chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "dev": true,
- "requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "fsevents": "^1.2.7",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- }
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- },
- "del": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
- "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
- "dev": true,
- "requires": {
- "@types/glob": "^7.1.1",
- "globby": "^6.1.0",
- "is-path-cwd": "^2.0.0",
- "is-path-in-cwd": "^2.0.0",
- "p-map": "^2.0.0",
- "pify": "^4.0.1",
- "rimraf": "^2.6.3"
- }
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "globby": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
- "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
- "dev": true,
- "requires": {
- "array-union": "^1.0.1",
- "glob": "^7.0.3",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "dependencies": {
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- }
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "opn": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
- "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
- "dev": true,
- "requires": {
- "is-wsl": "^1.1.0"
- }
- },
- "p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "dev": true
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "dev": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "spdy": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz",
- "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==",
- "dev": true,
- "requires": {
- "debug": "^4.1.0",
- "handle-thing": "^2.0.0",
- "http-deceiver": "^1.2.7",
- "select-hose": "^2.0.0",
- "spdy-transport": "^3.0.0"
- }
+ "sockjs": "^0.3.24",
+ "spdy": "^4.0.2",
+ "webpack-dev-middleware": "^7.4.2",
+ "ws": "^8.18.0"
+ },
+ "bin": {
+ "webpack-dev-server": "bin/webpack-dev-server.js"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack": {
+ "optional": true
},
- "supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ipaddr.js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz",
+ "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ws": {
+ "version": "8.21.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
+ "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
},
- "yargs": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
- "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
- "dev": true,
- "requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^3.0.0",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
- "which-module": "^2.0.0",
- "y18n": "^3.2.1 || ^4.0.0",
- "yargs-parser": "^11.1.1"
- }
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
+ "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "flat": "^5.0.2",
+ "wildcard": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
+ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpackbar": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-7.0.0.tgz",
+ "integrity": "sha512-aS9soqSO2iCHgqHoCrj4LbfGQUboDCYJPSFOAchEK+9psIjNrfSWW4Y0YEz67MKURNvMmfo0ycOg9d/+OOf9/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansis": "^3.2.0",
+ "consola": "^3.2.3",
+ "pretty-time": "^1.1.0",
+ "std-env": "^3.7.0"
+ },
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "peerDependencies": {
+ "@rspack/core": "*",
+ "webpack": "3 || 4 || 5"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
},
- "yargs-parser": {
- "version": "11.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
- "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
+ "webpack": {
+ "optional": true
}
}
},
- "webpack-hot-middleware": {
- "version": "2.25.0",
- "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.0.tgz",
- "integrity": "sha512-xs5dPOrGPCzuRXNi8F6rwhawWvQQkeli5Ro48PRuQh8pYPCPmNnltP9itiUPT4xI8oW+y0m59lyyeQk54s5VgA==",
+ "node_modules/websocket-driver": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz",
+ "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
"dev": true,
- "requires": {
- "ansi-html": "0.0.7",
- "html-entities": "^1.2.0",
- "querystring": "^0.2.0",
- "strip-ansi": "^3.0.0"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
}
},
- "webpack-log": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
- "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"dev": true,
- "requires": {
- "ansi-colors": "^3.0.0",
- "uuid": "^3.3.2"
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
}
},
- "webpack-merge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
- "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"dev": true,
- "requires": {
- "lodash": "^4.17.15"
+ "engines": {
+ "node": ">=18"
}
},
- "webpack-sources": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
- "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dev": true,
- "requires": {
- "source-list-map": "^2.0.0",
- "source-map": "~0.6.1"
- },
"dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
}
},
- "webpack-stats-plugin": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/webpack-stats-plugin/-/webpack-stats-plugin-0.3.1.tgz",
- "integrity": "sha512-pxqzFE055NlNTlNyfDG3xlB2QwT1EWdm/CF5dCJI/e+rRHVxrWhWg1rf1lfsWhI1/EePv8gi/A36YxO/+u0FgQ==",
- "dev": true
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
},
- "websocket-driver": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
- "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
"dev": true,
- "requires": {
- "http-parser-js": ">=0.4.0 <0.4.11",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "websocket-extensions": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
- "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
- "dev": true
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "whatwg-fetch": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
- "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==",
+ "node_modules/which-builtin-type/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
- "whatwg-url": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
- "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
"dev": true,
- "requires": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^1.0.1",
- "webidl-conversions": "^4.0.2"
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "whet.extend": {
- "version": "0.9.9",
- "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
- "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=",
- "dev": true
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "which": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
- "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
- "requires": {
- "isexe": "^2.0.0"
+ "node_modules/widest-line": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+ "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "which-module": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "node_modules/wildcard": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
"dev": true
},
- "wide-align": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
- "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "widest-line": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
- "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
- "requires": {
- "string-width": "^2.1.1"
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "windows-release": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz",
- "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==",
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
- "execa": "^1.0.0"
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "node_modules/wrap-ansi-cjs/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
- "worker-farm": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
- "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+ "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
- "requires": {
- "errno": "~0.1.7"
+ "engines": {
+ "node": ">=8"
}
},
- "worker-rpc": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
- "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "microevent.ts": "~0.1.1"
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "engines": {
+ "node": ">=12"
},
- "dependencies": {
- "ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz",
- "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==",
- "dev": true,
- "requires": {
- "@types/color-name": "^1.1.1",
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "string-width": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
- "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- }
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- },
- "write": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
- "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
- "requires": {
- "mkdirp": "^0.5.1"
+ "engines": {
+ "node": ">=12"
},
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
"dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- }
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "write-file-atomic": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz",
- "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==",
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
"dev": true,
- "requires": {
- "graceful-fs": "^4.1.11",
+ "license": "ISC",
+ "dependencies": {
"imurmurhash": "^0.1.4",
- "signal-exit": "^3.0.2"
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
}
},
- "ws": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
- "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
- "requires": {
+ "node_modules/ws": {
+ "version": "6.2.5",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.5.tgz",
+ "integrity": "sha512-T7pPl+DnmNrKRuttAIQwueReX5GqsedYEWp3/H//CG35+DyUOe1+/voAeE8idxgfLsQf2tO+rdtBd1FnPopgTQ==",
+ "license": "MIT",
+ "dependencies": {
"async-limiter": "~1.0.0"
}
},
- "xdg-basedir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
- "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
- "dev": true
+ "node_modules/wsl-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-wsl": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "xml": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
- "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=",
+ "node_modules/xcase": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz",
+ "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==",
"dev": true
},
- "xmlcreate": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz",
- "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==",
- "dev": true
+ "node_modules/xdg-basedir": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
+ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "xmldom": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.2.1.tgz",
- "integrity": "sha512-kXXiYvmblIgEemGeB75y97FyaZavx6SQhGppLw5TKWAD2Wd0KAly0g23eVLh17YcpxZpnFym1Qk/eaRjy1APPg=="
+ "node_modules/xml-js": {
+ "version": "1.6.11",
+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "xml-js": "bin/cli.js"
+ }
},
- "xmlhttprequest-ssl": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
- "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
- },
- "xpath": {
- "version": "0.0.27",
- "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
- "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ=="
- },
- "xregexp": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.2.4.tgz",
- "integrity": "sha512-sO0bYdYeJAJBcJA8g7MJJX7UrOZIfJPd8U2SC7B2Dd/J24U0aQNoGp33shCaBSWeb0rD5rh6VBUIXOkGal1TZA==",
- "requires": {
- "@babel/runtime-corejs2": "^7.2.0"
- }
- },
- "xstate": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.8.0.tgz",
- "integrity": "sha512-xHSYQtCHLkcrFRxa5lK4Lp1rnKt00a80jcKFMQiMBuE+6MvTYv7twwqYpzjsJoKFjGZB3GGEpZAuY1dmlPTh/g==",
+ "node_modules/xml-naming": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
+ "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/xmlcreate": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
+ "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
"dev": true
},
- "xtend": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
- "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ "node_modules/xmlhttprequest-ssl": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz",
+ "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
},
- "y18n": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
- "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
- "dev": true
+ "node_modules/xpath": {
+ "version": "0.0.34",
+ "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz",
+ "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==",
+ "engines": {
+ "node": ">=0.6.0"
+ }
},
- "yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
- "yaml": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz",
- "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==",
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.6.3"
- },
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": {
- "version": "7.7.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz",
- "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.2"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
- "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
- "dev": true
- }
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "yaml-loader": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.5.0.tgz",
- "integrity": "sha512-p9QIzcFSNm4mCw/m5NdyMfN4RE4aFZJWRRb01ERVNGCym8VNbKtw3OYZXnvUIkim6U/EjqE/2yIh9F/msShH9A==",
+ "node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
- "requires": {
- "js-yaml": "^3.5.2"
+ "engines": {
+ "node": ">=10"
}
},
- "yargs": {
- "version": "13.3.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
- "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true,
- "requires": {
- "cliui": "^5.0.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^3.0.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^13.1.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "cliui": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
- "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
- "dev": true,
- "requires": {
- "string-width": "^3.1.0",
- "strip-ansi": "^5.2.0",
- "wrap-ansi": "^5.1.0"
- }
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
- "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "wrap-ansi": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
- "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "string-width": "^3.0.0",
- "strip-ansi": "^5.0.0"
- }
- },
- "y18n": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
- "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
- "dev": true
- }
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "yargs-parser": {
- "version": "13.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
- "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "node_modules/yargs-unparser/node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
"dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
+ "engines": {
+ "node": ">=10"
},
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs-unparser/node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
- }
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "yargs-unparser": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
- "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "node_modules/yargs/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "requires": {
- "flat": "^4.1.0",
- "lodash": "^4.17.15",
- "yargs": "^13.3.0"
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
}
},
- "yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+ "node_modules/yauzl": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.2.tgz",
+ "integrity": "sha512-Md9ankxxN23wncAN8s7+Tn3Co52zLUPMtnrLAbVCnfG5d2tKBFfmygYSgXlqFgXObtzIgqkx7aNgDBpso9+4qA==",
"dev": true,
- "requires": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "yeast": {
+ "node_modules/yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
- "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+ "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
},
- "yn": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
- "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
- "dev": true
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "yoga-layout-prebuilt": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.3.tgz",
- "integrity": "sha512-9SNQpwuEh2NucU83i2KMZnONVudZ86YNcFk9tq74YaqrQfgJWO3yB9uzH1tAg8iqh5c9F5j0wuyJ2z72wcum2w==",
+ "node_modules/zod": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
+ "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"dev": true,
- "optional": true
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
},
- "yurnalist": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/yurnalist/-/yurnalist-1.1.2.tgz",
- "integrity": "sha512-y7bsTXqL+YMJQ2De2CBtSftJNLQnB7gWIzzKm10GDyC8Fg4Dsmd2LG5YhT8pudvUiuotic80WVXt/g1femRVQg==",
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
"dev": true,
- "requires": {
- "babel-runtime": "^6.26.0",
- "chalk": "^2.4.2",
- "cli-table3": "^0.5.1",
- "debug": "^4.1.1",
- "deep-equal": "^1.1.0",
- "detect-indent": "^6.0.0",
- "inquirer": "^7.0.0",
- "invariant": "^2.2.0",
- "is-builtin-module": "^3.0.0",
- "is-ci": "^2.0.0",
- "leven": "^3.1.0",
- "loud-rejection": "^2.2.0",
- "node-emoji": "^1.10.0",
- "object-path": "^0.11.2",
- "read": "^1.0.7",
- "rimraf": "^3.0.0",
- "semver": "^6.3.0",
- "strip-ansi": "^5.2.0",
- "strip-bom": "^4.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "builtin-modules": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
- "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
- "dev": true
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "dev": true,
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "is-builtin-module": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.0.0.tgz",
- "integrity": "sha512-/93sDihsAD652hrMEbJGbMAVBf1qc96kyThHQ0CAOONHaE3aROLpTjDe4WQ5aoC5ITHFxEq1z8XqSU7km+8amw==",
- "dev": true,
- "requires": {
- "builtin-modules": "^3.0.0"
- }
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "dev": true,
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "loud-rejection": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz",
- "integrity": "sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==",
- "dev": true,
- "requires": {
- "currently-unhandled": "^0.4.1",
- "signal-exit": "^3.0.2"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
- }
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
- }
+ },
+ "vendor/http-deceiver": {}
}
}
diff --git a/package.json b/package.json
index ef8dafaa847c2..231e5c0255796 100644
--- a/package.json
+++ b/package.json
@@ -10,97 +10,100 @@
"image",
"shields.io"
],
- "homepage": "http://shields.io",
+ "homepage": "https://shields.io",
"bugs": {
- "url": "https://github.com/badges/shields/issues",
- "email": "thaddee.tyl@gmail.com"
+ "url": "https://github.com/badges/shields/issues"
},
- "license": "CC0-1.0",
+ "license": "(MIT OR Apache-2.0)",
"author": "Thaddée Tyl ",
+ "contributors": [
+ "Paul Melnikow",
+ "Pierre-Yves Bigourdan"
+ ],
"repository": {
"type": "git",
"url": "https://github.com/badges/shields"
},
"dependencies": {
- "@hapi/joi": "^16.1.8",
- "@sentry/node": "^5.15.5",
- "bytes": "^3.1.0",
- "camelcase": "^5.3.1",
- "@shields_io/camp": "^18.0.0",
- "chai-as-promised": "^7.1.1",
- "chalk": "^3.0.0",
- "check-node-version": "^4.0.3",
- "chrome-web-store-item-property": "~1.2.0",
- "config": "^3.3.1",
- "cross-env": "^6.0.3",
- "decamelize": "^3.2.0",
- "dotenv": "^8.2.0",
- "emojic": "^1.1.15",
- "escape-string-regexp": "^2.0.0",
- "fast-xml-parser": "^3.16.0",
- "fsos": "^1.1.6",
+ "@renovatebot/pep440": "^5.0.0",
+ "@renovatebot/ruby-semver": "^5.0.0",
+ "@sentry/node-core": "^10.67.0",
+ "@shields_io/camp": "^18.1.2",
+ "@xmldom/xmldom": "0.9.10",
"badge-maker": "file:badge-maker",
- "glob": "^7.1.6",
- "graphql": "^14.6.0",
- "graphql-tag": "^2.10.3",
- "ioredis": "4.16.3",
- "joi-extension-semver": "4.0.0",
- "js-yaml": "^3.13.1",
- "jsonpath": "~1.0.2",
+ "byte-size": "^9.0.1",
+ "camelcase": "^9.0.0",
+ "chalk": "^5.6.2",
+ "check-node-version": "^4.2.1",
+ "cloudflare-middleware": "^1.0.4",
+ "config": "^4.4.2",
+ "cross-env": "^10.1.0",
+ "dayjs": "^1.11.21",
+ "decamelize": "^6.0.1",
+ "emojic": "^1.1.18",
+ "escape-string-regexp": "^5.0.0",
+ "fast-xml-parser": "^5.10.1",
+ "glob": "^13.0.6",
+ "global-agent": "^4.1.3",
+ "got": "^15.1.0",
+ "graphql": "17.0.2",
+ "graphql-tag": "^2.12.7",
+ "joi": "18.2.3",
+ "joi-extension-semver": "5.0.0",
+ "js-yaml": "^5.2.1",
+ "jsonpath-plus": "^10.4.0",
"lodash.countby": "^4.6.0",
- "lodash.groupby": "^4.6.0",
- "lodash.times": "^4.3.2",
- "moment": "^2.24.0",
- "node-env-flag": "^0.1.0",
- "parse-link-header": "^1.0.1",
- "path-to-regexp": "^5.0.0",
- "pretty-bytes": "^5.3.0",
- "priorityqueuejs": "^1.0.0",
- "prom-client": "^11.5.3",
- "query-string": "^6.12.1",
- "request": "~2.88.2",
- "semver": "~7.3.2",
- "simple-icons": "2.9.0",
- "xmldom": "~0.2.1",
- "xpath": "~0.0.27"
+ "matcher": "^6.0.0",
+ "mvncmp": "^1.0.0",
+ "node-pg-migrate": "^9.0.0",
+ "parse-link-header": "^2.0.0",
+ "path-to-regexp": "^6.3.0",
+ "pg": "^8.22.0",
+ "priorityqueuejs": "^2.0.0",
+ "prom-client": "^15.1.3",
+ "qs": "^6.15.3",
+ "re2": "^1.26.1",
+ "semver": "~7.8.5",
+ "simple-icons": "16.26.0",
+ "smol-toml": "1.7.0",
+ "svg-path-bbox": "^2.1.0",
+ "svgpath": "^2.6.0",
+ "webextension-store-meta": "^1.3.0",
+ "xpath": "~0.0.34"
},
"scripts": {
- "coverage:test:core": "nyc npm run test:core",
- "coverage:test:frontend": "nyc --nycrc-path .nycrc-frontend.json npm run test:frontend",
- "coverage:test:package": "nyc npm run test:package",
- "coverage:test:entrypoint": "nyc npm run test:entrypoint",
- "coverage:test:integration": "nyc npm run test:integration",
- "coverage:test:services": "nyc npm run test:services",
- "coverage:clean": "rimraf .nyc_output coverage",
- "precoverage:test": "run-s --silent coverage:clean defs features",
- "coverage:test": "run-s --silent --continue-on-error coverage:test:core coverage:test:package coverage:test:entrypoint coverage:test:frontend coverage:test:integration",
- "coverage:report:generate": "nyc report",
+ "coverage:test:core": "c8 npm run test:core",
+ "coverage:test:package": "c8 npm run test:package",
+ "coverage:test:entrypoint": "c8 npm run test:entrypoint",
+ "coverage:test:integration": "c8 npm run test:integration",
+ "coverage:test:services": "c8 npm run test:services",
+ "coverage:clean": "rimraf coverage",
+ "precoverage:test": "cross-env BASE_URL=http://localhost:8080 run-s --silent coverage:clean defs",
+ "coverage:test": "run-s --silent --continue-on-error coverage:test:core coverage:test:package coverage:test:entrypoint coverage:test:integration",
+ "coverage:report:generate": "c8 report",
"coverage:report:open": "open-cli coverage/lcov-report/index.html",
"coverage:report": "run-s --silent coverage:report:generate coverage:report:open",
- "lint": "eslint \"**/*.@(js|ts|tsx)\"",
- "prettier": "prettier --write \"**/*.@(js|ts|tsx|md|json|yml)\"",
- "prettier:check": "prettier --check \"**/*.@(js|ts|tsx|md|json|yml)\"",
+ "lint": "eslint --concurrency auto \"**/*.@(cjs|mjs|js|ts)\"",
+ "prettier": "prettier --write \"**/*.@(cjs|mjs|js|ts|md|json|yml)\"",
+ "prettier:check": "prettier --check \"**/*.@(cjs|mjs|js|ts|md|json|yml)\"",
"danger": "danger",
- "test:frontend": "cross-env NODE_ENV=test ts-mocha --config .mocharc-frontend.yml \"frontend/**/*.spec.@(js|ts|tsx)\"",
"test:e2e": "cypress run",
- "test:core": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.spec.js\" \"lib/**/*.spec.js\" \"services/**/*.spec.js\"",
+ "test:core": "cross-env TZ=UTC NODE_CONFIG_ENV=test mocha \"core/**/*.spec.js\" \"lib/**/*.spec.js\" \"services/**/*.spec.js\"",
"test:package": "mocha \"badge-maker/**/*.spec.js\"",
"test:entrypoint": "cross-env NODE_CONFIG_ENV=test mocha entrypoint.spec.js",
"test:integration": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.integration.js\" \"services/**/*.integration.js\"",
"test:services": "cross-env NODE_CONFIG_ENV=test mocha core/service-test-runner/cli.js",
- "test:services:trace": "cross-env NODE_CONFIG_ENV=test TRACE_SERVICES=true npm run test:services -- $*",
+ "test:services:trace": "cross-env NODE_CONFIG_ENV=test TRACE_SERVICES=true mocha core/service-test-runner/cli.js",
"test:services:pr:prepare": "node core/service-test-runner/pull-request-services-cli.js > pull-request-services.log",
"test:services:pr:run": "cross-env NODE_CONFIG_ENV=test mocha core/service-test-runner/cli.js --stdin < pull-request-services.log",
"test:services:pr": "run-s --silent test:services:pr:prepare test:services:pr:run",
- "pretest": "run-s --silent defs features",
- "test": "run-s --silent --continue-on-error lint test:frontend test:package test:core test:entrypoint prettier:check",
- "check-types:frontend": "tsc --noEmit --project .",
- "depcheck": "check-node-version --node \">= 8.0\"",
- "postinstall": "run-s --silent depcheck",
+ "pretest": "cross-env BASE_URL=http://localhost:8080 run-s --silent defs",
+ "test": "run-s --silent --continue-on-error lint test:package test:core test:entrypoint check-types:package prettier:check",
+ "check-types:package": "tsd badge-maker",
+ "depcheck": "check-node-version --node \">= 24.0\"",
"prebuild": "run-s --silent depcheck",
- "features": "node scripts/export-supported-features-cli.js > supported-features.json",
- "defs": "node scripts/export-service-definitions-cli.js > service-definitions.yml",
- "build": "run-s defs features && gatsby build",
+ "defs": "node scripts/export-openapi-cli.js",
+ "build": "rimraf public && run-s defs docusaurus:build",
"heroku-postbuild": "run-s --silent build",
"start:server:prod": "node server",
"now-start": "npm run start:server:prod",
@@ -109,177 +112,114 @@
"debug:server": "cross-env NODE_CONFIG_ENV=development nodemon --inspect server.js 8080",
"profile:server": "cross-env NODE_CONFIG_ENV=development node --prof server 8080",
"benchmark:badge": "cross-env NODE_CONFIG_ENV=test node scripts/benchmark-performance.js --iterations 10100 | node scripts/capture-timings.js --warmup-iterations 100",
- "prestart": "run-s --silent depcheck defs features",
- "start": "concurrently --names server,frontend \"npm run start:server\" \"cross-env GATSBY_BASE_URL=http://localhost:8080 gatsby develop --port 3000\"",
- "e2e": "start-server-and-test start http://localhost:3000 test:e2e",
- "e2e-on-build": "cross-env CYPRESS_baseUrl=http://localhost:8080 start-server-and-test start:server:e2e-on-build http://localhost:8080 test:e2e",
+ "prestart": "cross-env BASE_URL=http://localhost:8080 run-s --silent depcheck defs",
+ "start": "concurrently --names server,frontend \"npm run start:server\" \"npm run docusaurus:start\"",
+ "e2e": "cross-env BASE_URL=http://localhost:8080 run-s build e2e-on-build",
+ "e2e-on-build": "cross-env CYPRESS_BASE_URL=http://localhost:8080 start-server-and-test start:server:e2e-on-build http://localhost:8080 test:e2e",
"badge": "cross-env NODE_CONFIG_ENV=test TRACE_SERVICES=true node scripts/badge-cli.js",
- "build-docs": "rimraf api-docs/ && jsdoc --pedantic -c ./jsdoc.json ."
+ "build-docs": "rimraf api-docs/ && jsdoc --pedantic -c ./jsdoc.json . && echo 'contributing.shields.io' > api-docs/CNAME",
+ "migrate": "node scripts/write-migrations-config.js > migrations-config.json && node-pg-migrate --config-file=migrations-config.json",
+ "docusaurus:start": "docusaurus start frontend",
+ "docusaurus:build": "docusaurus build frontend --out-dir ../public",
+ "docusaurus:swizzle": "cd frontend && docusaurus swizzle",
+ "docusaurus:clear": "docusaurus clear frontend"
},
"lint-staged": {
- "**/*.js": [
- "eslint --fix",
- "prettier --write",
- "git add"
+ "**/*.@(cjs|mjs|js|ts)": [
+ "eslint --fix --concurrency auto",
+ "prettier --write"
],
"**/*.@(md|json|yml)": [
- "prettier --write",
- "git add"
+ "prettier --write"
]
},
"nodemonConfig": {
"verbose": true,
- "ext": "js",
+ "ext": "js,yml",
"ignore": [
"package.json",
"**/*.spec.js",
"**/*.tester.js",
"**/*.integration.js",
"frontend/",
- "public/",
"build/",
- "cypress/"
+ "cypress/",
+ ".github/"
]
},
"devDependencies": {
- "@babel/core": "^7.9.0",
- "@babel/polyfill": "^7.8.7",
- "@babel/register": "7.9.0",
- "@mapbox/react-click-to-select": "^2.2.0",
- "@types/chai": "^4.2.11",
- "@types/chai-enzyme": "^0.6.7",
- "@types/enzyme": "^3.10.5",
- "@types/lodash.debounce": "^4.0.6",
- "@types/lodash.groupby": "^4.6.6",
- "@types/mocha": "^7.0.2",
- "@types/node": "^13.13.2",
- "@types/react-helmet": "^5.0.15",
- "@types/react-modal": "^3.10.5",
- "@types/react-select": "^3.0.11",
- "@types/styled-components": "4.1.8",
- "@typescript-eslint/eslint-plugin": "^2.29.0",
- "@typescript-eslint/parser": "^2.29.0",
- "babel-plugin-inline-react-svg": "^1.1.1",
- "babel-plugin-istanbul": "^6.0.0",
- "babel-preset-gatsby": "^0.2.36",
- "caller": "^1.0.1",
- "chai": "^4.1.2",
- "chai-datetime": "^1.5.0",
- "chai-enzyme": "^1.0.0-beta.1",
- "chai-string": "^1.4.0",
- "cheerio": "^1.0.0-rc.3",
- "child-process-promise": "^2.2.1",
- "clipboard-copy": "^3.1.0",
- "concurrently": "^5.1.0",
- "cypress": "^4.4.1",
- "danger": "^10.1.1",
- "danger-plugin-no-test-shortcuts": "^2.0.0",
- "deepmerge": "^4.2.2",
- "enzyme": "^3.11.0",
- "enzyme-adapter-react-16": "^1.15.2",
- "eslint": "^6.8.0",
- "eslint-config-prettier": "^6.11.0",
- "eslint-config-standard": "^14.1.1",
- "eslint-config-standard-react": "^9.2.0",
- "eslint-plugin-chai-friendly": "^0.6.0",
- "eslint-plugin-cypress": "^2.10.3",
- "eslint-plugin-import": "^2.20.2",
- "eslint-plugin-jsdoc": "^20.4.0",
- "eslint-plugin-mocha": "^6.3.0",
- "eslint-plugin-no-extension-in-require": "^0.2.0",
- "eslint-plugin-node": "^11.1.0",
- "eslint-plugin-promise": "^4.2.1",
- "eslint-plugin-react": "^7.19.0",
- "eslint-plugin-react-hooks": "^2.5.1",
- "eslint-plugin-sort-class-members": "^1.7.0",
- "eslint-plugin-standard": "^4.0.1",
- "fetch-ponyfill": "^6.1.0",
- "fs-readfile-promise": "^3.0.1",
- "gatsby": "2.19.41",
- "gatsby-plugin-catch-links": "^2.1.28",
- "gatsby-plugin-page-creator": "^2.1.46",
- "gatsby-plugin-react-helmet": "^3.1.24",
- "gatsby-plugin-remove-trailing-slashes": "^2.1.24",
- "gatsby-plugin-styled-components": "^3.1.21",
- "gatsby-plugin-typescript": "^2.2.5",
- "got": "^9.6.0",
- "humanize-string": "^2.1.0",
- "husky": "^3.1.0",
- "icedfrisby": "3.0.0",
- "icedfrisby-nock": "^2.0.0",
- "is-png": "^2.0.0",
- "is-svg": "^4.2.1",
- "js-yaml-loader": "^1.2.2",
- "jsdoc": "^3.6.4",
- "lint-staged": "^9.5.0",
- "lodash.debounce": "^4.0.8",
- "lodash.difference": "^4.5.0",
- "minimist": "^1.2.5",
- "mocha": "^7.1.2",
- "mocha-env-reporter": "^4.0.0",
- "mocha-junit-reporter": "^1.23.3",
- "mocha-yaml-loader": "^1.0.3",
- "nock": "11.9.1",
- "node-mocks-http": "^1.8.1",
- "nodemon": "^2.0.3",
+ "@docusaurus/core": "^3.10.2",
+ "@docusaurus/preset-classic": "^3.10.2",
+ "@easyops-cn/docusaurus-search-local": "^0.55.2",
+ "@mdx-js/react": "^3.1.1",
+ "@typescript-eslint/parser": "^8.65.0",
+ "c8": "^12.0.0",
+ "caller": "^1.1.0",
+ "chai": "6.2.2",
+ "chai-as-promised": "^8.0.2",
+ "chai-datetime": "^1.8.1",
+ "clsx": "^2.1.1",
+ "concurrently": "^10.0.3",
+ "cypress": "^15.19.0",
+ "cypress-wait-for-stable-dom": "^0.1.0",
+ "danger": "^13.0.10",
+ "deepmerge": "^4.3.1",
+ "docusaurus-preset-openapi": "0.8.0",
+ "eslint": "9.39.2",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-chai-friendly": "1.2.1",
+ "eslint-plugin-cypress": "6.4.3",
+ "eslint-plugin-icedfrisby": "0.3.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-jsdoc": "63.2.0",
+ "eslint-plugin-mocha": "11.3.0",
+ "eslint-plugin-prettier": "5.5.6",
+ "eslint-plugin-promise": "7.3.0",
+ "eslint-plugin-react-hooks": "7.1.1",
+ "eslint-plugin-sort-class-members": "1.22.1",
+ "globals": "17.7.0",
+ "http-deceiver": "file:vendor/http-deceiver",
+ "icedfrisby": "4.0.0",
+ "icedfrisby-nock": "^2.1.0",
+ "is-svg": "^6.1.0",
+ "jsdoc": "^4.0.5",
+ "lint-staged": "^17.1.1",
+ "mocha": "^11.7.6",
+ "neostandard": "0.13.0",
+ "nock": "13.5.6",
+ "node-mocks-http": "^1.18.1",
+ "nodemon": "^3.1.14",
"npm-run-all": "^4.1.5",
- "nyc": "^15.0.1",
- "opn-cli": "^5.0.0",
- "portfinder": "^1.0.25",
- "prettier": "1.19.1",
- "react": "^16.13.1",
- "react-dom": "^16.13.1",
- "react-error-overlay": "^3.0.0",
- "react-helmet": "^5.2.1",
- "react-modal": "^3.11.2",
- "react-pose": "^4.0.10",
- "react-select": "^3.1.0",
- "read-all-stdin-sync": "^1.0.5",
- "redis-server": "^1.2.2",
- "require-hacker": "^3.0.1",
- "rimraf": "^3.0.2",
- "sazerac": "^1.1.0",
- "sinon": "^8.1.1",
- "sinon-chai": "^3.5.0",
- "snap-shot-it": "^7.9.3",
- "start-server-and-test": "1.10.7",
- "styled-components": "^5.1.0",
- "tmp": "0.1.0",
- "ts-mocha": "^7.0.0",
- "typescript": "^3.8.3"
+ "open-cli": "^9.0.0",
+ "portfinder": "^1.0.38",
+ "prettier": "3.9.6",
+ "prism-react-renderer": "^2.4.1",
+ "react": "^19.2.8",
+ "react-dom": "^19.2.8",
+ "rimraf": "^6.1.3",
+ "sazerac": "^2.0.0",
+ "simple-git-hooks": "^2.13.1",
+ "sinon": "^22.1.0",
+ "sinon-chai": "4.0.1",
+ "snap-shot-it": "^7.9.10",
+ "start-server-and-test": "3.0.11",
+ "tsd": "^0.33.0",
+ "url": "^0.11.4"
},
- "engines": {
- "node": "^10.16.3",
- "npm": "^6.11.3"
+ "overrides": {
+ "http-deceiver": "$http-deceiver"
},
- "babel": {
- "plugins": [
- [
- "inline-react-svg",
- {
- "svgo": false
- }
- ]
- ],
- "presets": [
- "babel-preset-gatsby"
- ],
- "env": {
- "test": {
- "plugins": [
- "istanbul"
- ]
- }
- }
+ "engines": {
+ "node": "^24 || ^26",
+ "npm": "^11 || ^12"
},
+ "type": "module",
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/shields",
"logo": "https://opencollective.com/opencollective/logo.txt"
},
- "husky": {
- "hooks": {
- "pre-commit": "lint-staged"
- }
+ "simple-git-hooks": {
+ "pre-commit": "npx lint-staged"
}
}
diff --git a/readme-logo.svg b/readme-logo.svg
new file mode 100644
index 0000000000000..20d4da48b4add
--- /dev/null
+++ b/readme-logo.svg
@@ -0,0 +1 @@
+Shields.io
diff --git a/scripts/badge-cli.js b/scripts/badge-cli.js
index 7fd060e6e801b..8bc36f907bc07 100644
--- a/scripts/badge-cli.js
+++ b/scripts/badge-cli.js
@@ -1,11 +1,9 @@
-'use strict'
-
-const { URL } = require('url')
-const config = require('config').util.toObject()
-const got = require('got')
-const emojic = require('emojic')
-const Server = require('../core/server/server')
-const trace = require('../core/base-service/trace')
+import { fileURLToPath, URL } from 'url'
+import config from 'config'
+import got from 'got'
+import emojic from 'emojic'
+import Server from '../core/server/server.js'
+import trace from '../core/base-service/trace.js'
function normalizeBadgeUrl(url) {
// Provide a base URL in order to accept fragments.
@@ -18,19 +16,18 @@ function normalizeBadgeUrl(url) {
}
async function traceBadge(badgeUrl) {
- const server = new Server(config)
+ const server = new Server(config.util.toObject())
await server.start()
- const { body } = await got(
+ const body = await got(
`${server.baseUrl.replace(/\/$/, '')}${badgeUrl}`,
- { json: true }
- )
+ ).json()
trace.logTrace('outbound', emojic.shield, 'Rendered badge', body)
await server.stop()
}
async function main() {
if (process.argv.length < 3) {
- console.error(`Usage: node ${__filename} badge-url`)
+ console.error(`Usage: node ${fileURLToPath(import.meta.url)} badge-url`)
process.exit(1)
}
const [, , url] = process.argv
diff --git a/scripts/benchmark-performance.js b/scripts/benchmark-performance.js
index 7686138f7ceac..356c9b3823aec 100644
--- a/scripts/benchmark-performance.js
+++ b/scripts/benchmark-performance.js
@@ -1,16 +1,15 @@
-'use strict'
-
-const config = require('config').util.toObject()
-const got = require('got')
-const minimist = require('minimist')
-const Server = require('../core/server/server')
+import { parseArgs } from 'util'
+import config from 'config'
+import got from 'got'
+import Server from '../core/server/server.js'
async function main() {
- const server = new Server(config)
+ const server = new Server(config.util.toObject())
await server.start()
- const args = minimist(process.argv)
- const iterations = parseInt(args.iterations) || 10000
- for (let i = 0; i < iterations; ++i) {
+ const { iterations = '10000' } = parseArgs({
+ options: { iterations: { type: 'string' } },
+ }).values
+ for (let i = 0; i < parseInt(iterations); ++i) {
await got(`${server.baseUrl}badge/coverage-${i}-green.svg`)
}
await server.stop()
diff --git a/scripts/capture-timings.js b/scripts/capture-timings.js
index fee8bf24a27a0..814c84581b703 100644
--- a/scripts/capture-timings.js
+++ b/scripts/capture-timings.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const readline = require('readline')
-const minimist = require('minimist')
+import readline from 'readline'
+import { parseArgs } from 'util'
async function captureTimings(warmupIterations) {
const rl = readline.createInterface({
@@ -31,22 +29,24 @@ async function captureTimings(warmupIterations) {
function logResults({ times, iterations, warmupIterations }) {
if (isNaN(iterations)) {
console.log(
- `No timings captured. Have you included console.time statements in the badge creation code path?`
+ 'No timings captured. Have you included console.time statements in the badge creation code path?',
)
} else {
const timedIterations = iterations - warmupIterations
for (const [label, time] of Object.entries(times)) {
const averageTime = time / timedIterations
console.log(
- `Average '${label}' time over ${timedIterations} iterations: ${averageTime}ms`
+ `Average '${label}' time over ${timedIterations} iterations: ${averageTime}ms`,
)
}
}
}
async function main() {
- const args = minimist(process.argv)
- const warmupIterations = parseInt(args['warmup-iterations']) || 100
+ const { 'warmup-iterations': warmupIter = '100' } = parseArgs({
+ options: { 'warmup-iterations': { type: 'string' } },
+ }).values
+ const warmupIterations = parseInt(warmupIter)
const { times, iterations } = await captureTimings(warmupIterations)
logResults({ times, iterations, warmupIterations })
}
diff --git a/scripts/export-openapi-cli.js b/scripts/export-openapi-cli.js
new file mode 100644
index 0000000000000..fa2e23a86710b
--- /dev/null
+++ b/scripts/export-openapi-cli.js
@@ -0,0 +1,53 @@
+import fs from 'fs'
+import path from 'path'
+import * as yaml from 'js-yaml'
+import { collectDefinitions } from '../core/base-service/loader.js'
+import { category2openapi } from '../core/base-service/openapi.js'
+
+const specsPath = path.join('frontend', 'categories')
+
+function writeSpec(filename, spec) {
+ // Omit undefined
+ // https://github.com/nodeca/js-yaml/issues/356#issuecomment-312430599
+ const cleaned = JSON.parse(JSON.stringify(spec))
+
+ fs.writeFileSync(
+ filename,
+ yaml.dump(cleaned, { flowLevel: 5, quoteStyle: 'double' }),
+ )
+}
+
+;(async () => {
+ const definitions = await collectDefinitions()
+
+ for (const category of definitions.categories) {
+ const services = definitions.services.filter(
+ service => service.category === category.id && !service.isRetired,
+ )
+
+ writeSpec(
+ path.join(specsPath, `${category.id}.yaml`),
+ category2openapi({ category, services, sort: true }),
+ )
+ }
+
+ let coreServices = []
+ coreServices = coreServices.concat(
+ definitions.services.filter(
+ service => service.category === 'static' && !service.isRetired,
+ ),
+ )
+ coreServices = coreServices.concat(
+ definitions.services.filter(
+ service => service.category === 'dynamic' && !service.isRetired,
+ ),
+ )
+ writeSpec(
+ path.join(specsPath, '1core.yaml'),
+ category2openapi({
+ category: { name: 'Core' },
+ services: coreServices,
+ sort: false,
+ }),
+ )
+})()
diff --git a/scripts/export-service-definitions-cli.js b/scripts/export-service-definitions-cli.js
deleted file mode 100644
index e761abb8b8f1e..0000000000000
--- a/scripts/export-service-definitions-cli.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict'
-
-const yaml = require('js-yaml')
-const { collectDefinitions } = require('../core/base-service/loader')
-
-const definitions = collectDefinitions()
-
-// Omit undefined
-// https://github.com/nodeca/js-yaml/issues/356#issuecomment-312430599
-const cleaned = JSON.parse(JSON.stringify(definitions))
-
-process.stdout.write(yaml.safeDump(cleaned, { flowLevel: 5 }))
diff --git a/scripts/export-supported-features-cli.js b/scripts/export-supported-features-cli.js
deleted file mode 100644
index a3dfb61486d58..0000000000000
--- a/scripts/export-supported-features-cli.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const logos = require('../lib/load-logos')()
-const simpleIcons = require('../lib/load-simple-icons')()
-
-const shieldsLogos = Object.keys(logos)
-
-const simpleIconSet = new Set(Object.keys(simpleIcons))
-shieldsLogos.forEach(logo => simpleIconSet.delete(logo))
-const simpleIconNames = Array.from(simpleIconSet)
-
-const supportedFeatures = {
- shieldsLogos,
- simpleIcons: simpleIconNames,
- advertisedStyles: [
- 'plastic',
- 'flat',
- 'flat-square',
- 'for-the-badge',
- 'social',
- ],
-}
-
-console.log(JSON.stringify(supportedFeatures, null, 2))
diff --git a/scripts/github_token_backup.fish b/scripts/github_token_backup.fish
deleted file mode 100755
index 8aef250809784..0000000000000
--- a/scripts/github_token_backup.fish
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env fish
-#
-# Back up the GitHub tokens from each production server.
-#
-
-if test (count $argv) -lt 1
- echo Usage: (basename (status -f)) shields_secret
-end
-
-set shields_secret $argv[1]
-
-function do_backup
- set server $argv[1]
- curl --insecure -u ":$shields_secret" "https://$server.servers.shields.io/\$github-auth/tokens" > "$server""_tokens.json"
-end
-
-for server in s0 s1 s2
- do_backup $server
-end
diff --git a/scripts/import-github-tokens.js b/scripts/import-github-tokens.js
deleted file mode 100644
index 3ab91e79f929a..0000000000000
--- a/scripts/import-github-tokens.js
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict'
-
-const { promises: fs } = require('fs')
-const Redis = require('ioredis')
-
-const key = 'githubUserTokens'
-
-async function loadTokens() {
- const contents = await fs.readFile('all_tokens_uniq.json', 'utf8')
- const tokens = JSON.parse(contents)
- console.log(`${tokens.length} tokens loaded`)
- return tokens
-}
-
-function createClient() {
- const redis = new Redis(process.env.REDIS_URL, {
- tls: { servername: new URL(process.env.REDIS_URL).hostname },
- })
- redis.on('error', err => {
- console.error(err)
- })
- return redis
-}
-
-async function load() {
- const redis = createClient()
- const tokens = await loadTokens()
- await redis.sadd(key, tokens)
- await redis.quit()
-}
-
-async function list() {
- const redis = createClient()
- const tokens = await redis.smembers(key)
- console.log(`${tokens.length} tokens loaded`)
- await redis.quit()
-}
-
-;(async () => {
- try {
- // await load()
- await list()
- } catch (e) {
- console.error(e)
- process.exit(1)
- }
-})()
-
-// Appease the linter.
-module.exports = { load, list }
diff --git a/scripts/mocha2md.js b/scripts/mocha2md.js
new file mode 100644
index 0000000000000..37963f6668663
--- /dev/null
+++ b/scripts/mocha2md.js
@@ -0,0 +1,47 @@
+import fs from 'fs'
+
+let data
+let title
+
+try {
+ if (process.argv.length < 4) {
+ throw new Error()
+ }
+ title = process.argv[2]
+ data = JSON.parse(fs.readFileSync(process.argv[3]))
+} catch (e) {
+ process.stdout.write('failed to write summary\n')
+ process.exit(1)
+}
+
+process.stdout.write(`# ${title}\n\n`)
+
+if (data.stats.passes > 0) {
+ process.stdout.write(`✔ ${data.stats.passes} passed\n`)
+}
+if (data.stats.failures > 0) {
+ process.stdout.write(`✖ ${data.stats.failures} failed\n`)
+}
+if (data.stats.pending > 0) {
+ process.stdout.write(`● ${data.stats.pending} pending\n`)
+}
+process.stdout.write('\n')
+
+if (data.stats.failures > 0) {
+ process.stdout.write('## Failures\n\n')
+ for (const test of data.failures) {
+ if (test.err && Object.keys(test.err).length > 0) {
+ process.stdout.write(`${test.fullTitle}\n\n`)
+ process.stdout.write('```\n')
+ process.stdout.write(`${test.err.stack}\n`)
+ process.stdout.write('```\n\n')
+ }
+ }
+}
+
+if (data.stats.pending > 0) {
+ process.stdout.write('## Pending\n\n')
+ for (const test of data.pending) {
+ process.stdout.write(`${test.fullTitle}\n\n`)
+ }
+}
diff --git a/scripts/redis-connectivity-test.js b/scripts/redis-connectivity-test.js
deleted file mode 100644
index 2aaf4baffec29..0000000000000
--- a/scripts/redis-connectivity-test.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const config = require('config').util.toObject()
-console.log(config)
-const GithubConstellation = require('../services/github/github-constellation')
-
-const { persistence } = new GithubConstellation({
- persistence: config.public.persistence,
- service: config.public.services.github,
- private: config.private,
-})
-
-async function main() {
- const tokens = await persistence.initialize()
- console.log(`${tokens.length} tokens loaded`)
- await persistence.stop()
-}
-
-;(async () => {
- try {
- await main()
- } catch (e) {
- console.error(e)
- process.exit(1)
- }
-})()
diff --git a/scripts/update-github-api.js b/scripts/update-github-api.js
new file mode 100644
index 0000000000000..578afc56604ad
--- /dev/null
+++ b/scripts/update-github-api.js
@@ -0,0 +1,19 @@
+import fs from 'fs/promises'
+import got from 'got'
+import * as yaml from 'js-yaml'
+
+const resp = await got('https://api.github.com/versions').json()
+const latestDate = resp.sort()[resp.length - 1]
+
+const config = yaml.load(await fs.readFile('./config/default.yml', 'utf8'))
+
+if (latestDate === config.public.services.github.restApiVersion) {
+ console.log("We're already using the latest version. No change needed.")
+ process.exit(0)
+}
+
+config.public.services.github.restApiVersion = latestDate
+await fs.writeFile(
+ './config/default.yml',
+ yaml.dump(config, { quoteStyle: 'double' }),
+)
diff --git a/scripts/write-migrations-config.js b/scripts/write-migrations-config.js
new file mode 100644
index 0000000000000..f98e265587f59
--- /dev/null
+++ b/scripts/write-migrations-config.js
@@ -0,0 +1,11 @@
+import configModule from 'config'
+const config = configModule.util.toObject()
+
+const postgresUrl = config?.private?.postgres_url
+
+if (!postgresUrl) {
+ process.exit(1)
+}
+
+process.stdout.write(JSON.stringify({ url: postgresUrl }))
+process.exit(0)
diff --git a/server.js b/server.js
index cb7055612a0c4..0158866c42605 100644
--- a/server.js
+++ b/server.js
@@ -1,26 +1,21 @@
-'use strict'
-/* eslint-disable import/order */
-
-const fs = require('fs')
-const path = require('path')
-
-require('dotenv').config()
+import configModule from 'config'
+import * as Sentry from '@sentry/node-core/light'
+import Server from './core/server/server.js'
// Set up Sentry reporting as early in the process as possible.
-const config = require('config').util.toObject()
-const Sentry = require('@sentry/node')
+const config = configModule.util.toObject()
const disabledIntegrations = ['Console', 'Http']
Sentry.init({
dsn: process.env.SENTRY_DSN || config.private.sentry_dsn,
integrations: integrations => {
const filtered = integrations.filter(
- integration => !disabledIntegrations.includes(integration.name)
+ integration => !disabledIntegrations.includes(integration.name),
)
if (filtered.length !== integrations.length - disabledIntegrations.length) {
throw Error(
- `An error occurred while filtering integrations. The following inetgrations were found: ${integrations.map(
- ({ name }) => name
- )}`
+ `An error occurred while filtering integrations. The following integrations were found: ${integrations.map(
+ ({ name }) => name,
+ )}`,
)
}
return filtered
@@ -37,22 +32,11 @@ if (process.argv[3]) {
console.log('Configuration:')
console.dir(config.public, { depth: null })
-const legacySecretsPath = path.join(__dirname, 'private', 'secret.json')
-if (fs.existsSync(legacySecretsPath)) {
- console.error(
- `Legacy secrets file found at ${legacySecretsPath}. It should be deleted and secrets replaced with environment variables or config/local.yml`
- )
- process.exit(1)
-}
+export const server = new Server(config)
-const Server = require('./core/server/server')
-const server = (module.exports = new Server(config))
+process.on('SIGTERM', async () => {
+ console.log('SIGTERM received, shutting down...')
+ await server.stop()
+})
-;(async () => {
- try {
- await server.start()
- } catch (e) {
- console.error(e)
- process.exit(1)
- }
-})()
+await server.start()
diff --git a/services/amo/amo-base.js b/services/amo/amo-base.js
index 72e1023e6aefb..36771f8a6ea92 100644
--- a/services/amo/amo-base.js
+++ b/services/amo/amo-base.js
@@ -1,10 +1,9 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const keywords = ['amo', 'firefox']
+const description =
+ '[addons.mozilla.org](https://addons.mozilla.org) (AMO) publishes extensions for Mozilla Firefox'
const schema = Joi.object({
average_daily_users: nonNegativeInteger,
@@ -18,16 +17,14 @@ const schema = Joi.object({
}).required()
class BaseAmoService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'mozilla add-on' }
- }
+ static defaultBadgeData = { label: 'mozilla add-on' }
async fetch({ addonId }) {
return this._requestJson({
schema,
- url: `https://addons.mozilla.org/api/v3/addons/addon/${addonId}`,
+ url: `https://addons.mozilla.org/api/v4/addons/addon/${addonId}/`,
})
}
}
-module.exports = { BaseAmoService, keywords }
+export { BaseAmoService, description }
diff --git a/services/amo/amo-downloads.service.js b/services/amo/amo-downloads.service.js
index ed73cab3a03aa..3d041653f8740 100644
--- a/services/amo/amo-downloads.service.js
+++ b/services/amo/amo-downloads.service.js
@@ -1,51 +1,34 @@
-'use strict'
+import { renderDownloadsBadge } from '../downloads.js'
+import { retiredService, pathParams } from '../index.js'
+import { BaseAmoService, description as baseDescription } from './amo-base.js'
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { redirector } = require('..')
-const { BaseAmoService, keywords } = require('./amo-base')
+const description = `${baseDescription}
-const documentation = `
-
- Previously amo/d provided a “total downloads” badge. However,
- updates to the v3 API only
- give us weekly downloads. The route amo/d redirects to amo/dw.
-
+Previously \`amo/d\` provided a “total downloads” badge. However,
+[updates to the v3 API](https://github.com/badges/shields/issues/3079)
+only give us weekly downloads. The route \`amo/d\` redirects to \`amo/dw\`.
`
class AmoWeeklyDownloads extends BaseAmoService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'amo/dw',
- pattern: ':addonId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Mozilla Add-on',
- namedParams: { addonId: 'dustman' },
- staticPreview: this.render({ downloads: 120 }),
- keywords,
- documentation,
+ static category = 'downloads'
+ static route = { base: 'amo/dw', pattern: ':addonId' }
+
+ static openApi = {
+ '/amo/dw/{addonId}': {
+ get: {
+ summary: 'Mozilla Add-on Downloads',
+ description,
+ parameters: pathParams({ name: 'addonId', example: 'dustman' }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
- return {
- message: `${metric(downloads)}/week`,
- color: downloadCount(downloads),
- }
+ return renderDownloadsBadge({ downloads, interval: 'week' })
}
async handle({ addonId }) {
@@ -56,17 +39,15 @@ class AmoWeeklyDownloads extends BaseAmoService {
}
}
-const AmoLegacyRedirect = redirector({
+const AmoLegacyRedirect = retiredService({
category: 'downloads',
+ label: 'mozilla-add-on',
route: {
base: 'amo/d',
pattern: ':addonId',
},
- transformPath: ({ addonId }) => `/amo/dw/${addonId}`,
- dateAdded: new Date('2019-02-23'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
})
-module.exports = {
- AmoWeeklyDownloads,
- AmoLegacyRedirect,
-}
+export { AmoWeeklyDownloads, AmoLegacyRedirect }
diff --git a/services/amo/amo-downloads.tester.js b/services/amo/amo-downloads.tester.js
index c75e141bb45e0..4c4278b63aed2 100644
--- a/services/amo/amo-downloads.tester.js
+++ b/services/amo/amo-downloads.tester.js
@@ -1,21 +1,22 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = new ServiceTester({
+import { ServiceTester } from '../tester.js'
+import { isMetricOverTimePeriod } from '../test-validators.js'
+export const t = new ServiceTester({
id: 'AmoDownloads',
title: 'AmoDownloads',
pathPrefix: '/amo',
-}))
+})
t.create('Weekly Downloads')
- .get('/dw/dustman.json')
+ .get('/dw/duckduckgo-for-firefox.json')
.expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
t.create('Weekly Downloads (not found)')
.get('/dw/not-a-real-plugin.json')
.expectBadge({ label: 'downloads', message: 'not found' })
-t.create('/d URL should redirect to /dw')
- .get('/d/dustman.svg')
- .expectRedirect('/amo/dw/dustman.svg')
+t.create('/d URL should return deprecated badge')
+ .get('/d/dustman.json')
+ .expectBadge({
+ label: 'mozilla-add-on',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/amo/amo-rating.service.js b/services/amo/amo-rating.service.js
index 4a81e2820aa3b..3e8b2989fe52a 100644
--- a/services/amo/amo-rating.service.js
+++ b/services/amo/amo-rating.service.js
@@ -1,45 +1,38 @@
-'use strict'
+import { starRating } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { pathParams } from '../index.js'
+import { BaseAmoService, description } from './amo-base.js'
-const { starRating } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseAmoService, keywords } = require('./amo-base')
+export default class AmoRating extends BaseAmoService {
+ static category = 'rating'
+ static route = { base: 'amo', pattern: ':format(stars|rating)/:addonId' }
-module.exports = class AmoRating extends BaseAmoService {
- static get category() {
- return 'rating'
- }
-
- static get route() {
- return {
- base: 'amo',
- pattern: ':format(stars|rating)/:addonId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Mozilla Add-on',
- pattern: 'rating/:addonId',
- namedParams: { addonId: 'dustman' },
- staticPreview: this.render({ format: 'rating', rating: 4 }),
- keywords,
+ static openApi = {
+ '/amo/rating/{addonId}': {
+ get: {
+ summary: 'Mozilla Add-on Rating',
+ description,
+ parameters: pathParams({ name: 'addonId', example: 'dustman' }),
},
- {
- title: 'Mozilla Add-on',
- pattern: 'stars/:addonId',
- namedParams: { addonId: 'dustman' },
- staticPreview: this.render({ format: 'stars', rating: 4 }),
- keywords,
+ },
+ '/amo/stars/{addonId}': {
+ get: {
+ summary: 'Mozilla Add-on Stars',
+ description,
+ parameters: pathParams({ name: 'addonId', example: 'dustman' }),
},
- ]
+ },
}
+ static _cacheLength = 7200
+
static render({ format, rating }) {
- rating = Math.round(rating)
return {
label: format,
- message: format === 'stars' ? starRating(rating) : `${rating}/5`,
+ message:
+ format === 'stars'
+ ? starRating(rating)
+ : `${Math.round(rating * 10) / 10}/5`,
color: floorCountColor(rating, 2, 3, 4),
}
}
diff --git a/services/amo/amo-rating.tester.js b/services/amo/amo-rating.tester.js
index 55d83c436e2ff..dc327d15d69f9 100644
--- a/services/amo/amo-rating.tester.js
+++ b/services/amo/amo-rating.tester.js
@@ -1,14 +1,13 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isStarRating } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isStarRating } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Rating')
.get('/rating/IndieGala-Helper.json')
.expectBadge({
label: 'rating',
- message: Joi.string().regex(/^\d\/\d$/),
+ message: Joi.string().regex(/^\d(\.\d)?\/\d$/),
})
t.create('Stars')
diff --git a/services/amo/amo-users.service.js b/services/amo/amo-users.service.js
index 09dfad14a1383..cd850b2b597bd 100644
--- a/services/amo/amo-users.service.js
+++ b/services/amo/amo-users.service.js
@@ -1,42 +1,27 @@
-'use strict'
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import { BaseAmoService, description } from './amo-base.js'
-const { metric } = require('../text-formatters')
-const { BaseAmoService, keywords } = require('./amo-base')
+export default class AmoUsers extends BaseAmoService {
+ static category = 'downloads'
+ static route = { base: 'amo/users', pattern: ':addonId' }
-module.exports = class AmoUsers extends BaseAmoService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'amo/users',
- pattern: ':addonId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Mozilla Add-on',
- namedParams: { addonId: 'dustman' },
- staticPreview: this.render({ users: 750 }),
- keywords,
+ static openApi = {
+ '/amo/users/{addonId}': {
+ get: {
+ summary: 'Mozilla Add-on Users',
+ description,
+ parameters: pathParams({ name: 'addonId', example: 'dustman' }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'users',
- }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'users' }
- static render({ users }) {
- return {
- message: metric(users),
- color: 'blue',
- }
+ static render({ users: downloads }) {
+ return renderDownloadsBadge({ downloads, colorOverride: 'blue' })
}
async handle({ addonId }) {
diff --git a/services/amo/amo-users.tester.js b/services/amo/amo-users.tester.js
index a0f168ad0fa37..5355be2ce1c25 100644
--- a/services/amo/amo-users.tester.js
+++ b/services/amo/amo-users.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Users')
.get('/IndieGala-Helper.json')
diff --git a/services/amo/amo-version.service.js b/services/amo/amo-version.service.js
index 6691273523f13..50f74f8c5b28f 100644
--- a/services/amo/amo-version.service.js
+++ b/services/amo/amo-version.service.js
@@ -1,29 +1,19 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { pathParams } from '../index.js'
+import { BaseAmoService, description } from './amo-base.js'
-const { renderVersionBadge } = require('../version')
-const { BaseAmoService, keywords } = require('./amo-base')
+export default class AmoVersion extends BaseAmoService {
+ static category = 'version'
+ static route = { base: 'amo/v', pattern: ':addonId' }
-module.exports = class AmoVersion extends BaseAmoService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'amo/v',
- pattern: ':addonId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Mozilla Add-on',
- namedParams: { addonId: 'dustman' },
- staticPreview: renderVersionBadge({ version: '2.1.0' }),
- keywords,
+ static openApi = {
+ '/amo/v/{addonId}': {
+ get: {
+ summary: 'Mozilla Add-on Version',
+ description,
+ parameters: pathParams({ name: 'addonId', example: 'dustman' }),
},
- ]
+ },
}
async handle({ addonId }) {
diff --git a/services/amo/amo-version.tester.js b/services/amo/amo-version.tester.js
index 23dac4cb9aee8..001ae8f162082 100644
--- a/services/amo/amo-version.tester.js
+++ b/services/amo/amo-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Version')
- .get('/IndieGala-Helper.json')
- .expectBadge({
- label: 'mozilla add-on',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('Version').get('/night-video-tuner.json').expectBadge({
+ label: 'mozilla add-on',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('Version (not found)')
.get('/not-a-real-plugin.json')
diff --git a/services/ansible/ansible-collection-downloads.service.js b/services/ansible/ansible-collection-downloads.service.js
new file mode 100644
index 0000000000000..04ebd81f7df16
--- /dev/null
+++ b/services/ansible/ansible-collection-downloads.service.js
@@ -0,0 +1,46 @@
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const ansibleCollectionSchema = Joi.object({
+ // Ansible docs don't mention this but it appears in the API responses
+ download_count: Joi.number().required(),
+}).required()
+
+export default class AnsibleGalaxyCollectionDownloads extends BaseJsonService {
+ static category = 'downloads'
+ static route = { base: 'ansible/collection/d', pattern: ':namespace/:name' }
+
+ static openApi = {
+ '/ansible/collection/d/{namespace}/{name}': {
+ get: {
+ summary: 'Ansible Collection Downloads',
+ parameters: pathParams(
+ { name: 'namespace', example: 'community' },
+ { name: 'name', example: 'general' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'collection downloads' }
+
+ static render({ downloads }) {
+ return renderDownloadsBadge({ downloads })
+ }
+
+ async fetch({ namespace, name }) {
+ return this._requestJson({
+ schema: ansibleCollectionSchema,
+ url: `https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/index/${namespace}/${name}/`,
+ })
+ }
+
+ async handle({ namespace, name }) {
+ const { download_count: downloads } = await this.fetch({
+ namespace,
+ name,
+ })
+ return this.constructor.render({ downloads })
+ }
+}
diff --git a/services/ansible/ansible-collection-downloads.tester.js b/services/ansible/ansible-collection-downloads.tester.js
new file mode 100644
index 0000000000000..fe0ae59b21b58
--- /dev/null
+++ b/services/ansible/ansible-collection-downloads.tester.js
@@ -0,0 +1,16 @@
+import { ServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = new ServiceTester({
+ id: 'AnsibleGalaxyCollectionDownloads',
+ title: 'AnsibleGalaxyCollectionDownloads',
+ pathPrefix: '/ansible/collection/d',
+})
+
+t.create('collection downloads (valid)')
+ .get('/community/general.json')
+ .expectBadge({ label: 'collection downloads', message: isMetric })
+
+t.create('collection downloads (not found)')
+ .get('/not/real.json')
+ .expectBadge({ label: 'collection downloads', message: 'not found' })
diff --git a/services/ansible/ansible-collection-version.service.js b/services/ansible/ansible-collection-version.service.js
new file mode 100644
index 0000000000000..1a139891f30f0
--- /dev/null
+++ b/services/ansible/ansible-collection-version.service.js
@@ -0,0 +1,47 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const ansibleCollectionSchema = Joi.object({
+ highest_version: Joi.object({
+ version: Joi.string().required(),
+ }).required(),
+}).required()
+
+export default class AnsibleGalaxyCollectionVersion extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'ansible/collection/v', pattern: ':namespace/:name' }
+
+ static openApi = {
+ '/ansible/collection/v/{namespace}/{name}': {
+ get: {
+ summary: 'Ansible Collection Version',
+ parameters: pathParams(
+ { name: 'namespace', example: 'community' },
+ { name: 'name', example: 'general' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'galaxy' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async fetch({ namespace, name }) {
+ return this._requestJson({
+ schema: ansibleCollectionSchema,
+ url: `https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/index/${namespace}/${name}/`,
+ })
+ }
+
+ async handle({ namespace, name }) {
+ const { highest_version: highestVersion } = await this.fetch({
+ namespace,
+ name,
+ })
+ return this.constructor.render({ version: highestVersion.version })
+ }
+}
diff --git a/services/ansible/ansible-collection-version.tester.js b/services/ansible/ansible-collection-version.tester.js
new file mode 100644
index 0000000000000..61daf1df8c142
--- /dev/null
+++ b/services/ansible/ansible-collection-version.tester.js
@@ -0,0 +1,16 @@
+import { ServiceTester } from '../tester.js'
+import { isSemver } from '../test-validators.js'
+
+export const t = new ServiceTester({
+ id: 'AnsibleGalaxyCollectionVersion',
+ title: 'AnsibleGalaxyCollectionVersion',
+ pathPrefix: '/ansible/collection/v',
+})
+
+t.create('collection version (valid)')
+ .get('/community/general.json')
+ .expectBadge({ label: 'galaxy', message: isSemver })
+
+t.create('collection version (not found)')
+ .get('/not/real.json')
+ .expectBadge({ label: 'galaxy', message: 'not found' })
diff --git a/services/ansible/ansible-quality.service.js b/services/ansible/ansible-quality.service.js
deleted file mode 100644
index 3590e03f6a2c5..0000000000000
--- a/services/ansible/ansible-quality.service.js
+++ /dev/null
@@ -1,69 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { floorCount } = require('../color-formatters')
-const { BaseJsonService, InvalidResponse } = require('..')
-
-const ansibleContentSchema = Joi.object({
- quality_score: Joi.number()
- .allow(null)
- .required(),
-}).required()
-
-class AnsibleGalaxyContent extends BaseJsonService {
- async fetch({ projectId }) {
- const url = `https://galaxy.ansible.com/api/v1/content/${projectId}/`
- return this._requestJson({
- url,
- schema: ansibleContentSchema,
- })
- }
-}
-
-module.exports = class AnsibleGalaxyContentQualityScore extends AnsibleGalaxyContent {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'ansible/quality',
- pattern: ':projectId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Ansible Quality Score',
- namedParams: {
- projectId: '432',
- },
- staticPreview: this.render({ qualityScore: 4.125 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'quality' }
- }
-
- static render({ qualityScore }) {
- return {
- message: qualityScore,
- color: floorCount(qualityScore, 2, 3, 4),
- }
- }
-
- async handle({ projectId }) {
- const { quality_score: qualityScore } = await this.fetch({ projectId })
-
- if (qualityScore === null) {
- throw new InvalidResponse({
- prettyMessage: 'no score available',
- })
- }
-
- return this.constructor.render({ qualityScore })
- }
-}
diff --git a/services/ansible/ansible-quality.tester.js b/services/ansible/ansible-quality.tester.js
deleted file mode 100644
index 0db519f25cb84..0000000000000
--- a/services/ansible/ansible-quality.tester.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict'
-
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('quality score (valid)')
- .get('/432.json')
- .expectBadge({ label: 'quality', message: nonNegativeInteger })
-
-t.create('quality score (not found)')
- .get('/0101.json')
- .expectBadge({ label: 'quality', message: 'no score available' })
diff --git a/services/ansible/ansible-role.service.js b/services/ansible/ansible-role.service.js
index b0aca330031fd..396cc9b923afe 100644
--- a/services/ansible/ansible-role.service.js
+++ b/services/ansible/ansible-role.service.js
@@ -1,110 +1,50 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { downloadCount } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
const ansibleRoleSchema = Joi.object({
- download_count: nonNegativeInteger,
- name: Joi.string().required(),
- summary_fields: Joi.object({
- namespace: Joi.object({
- name: Joi.string().required(),
- }),
- }),
+ results: Joi.array()
+ .items(
+ Joi.object({
+ download_count: nonNegativeInteger,
+ }),
+ )
+ .required(),
}).required()
-class AnsibleGalaxyRole extends BaseJsonService {
- async fetch({ roleId }) {
- const url = `https://galaxy.ansible.com/api/v1/roles/${roleId}/`
- return this._requestJson({
- url,
- schema: ansibleRoleSchema,
- })
- }
-}
-
-class AnsibleGalaxyRoleDownloads extends AnsibleGalaxyRole {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'ansible/role/d',
- pattern: ':roleId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Ansible Role',
- namedParams: { roleId: '3078' },
- staticPreview: this.render({ downloads: 76 }),
+export default class AnsibleGalaxyRoleDownloads extends BaseJsonService {
+ static category = 'downloads'
+ static route = { base: 'ansible/role/d', pattern: ':namespace/:name' }
+
+ static openApi = {
+ '/ansible/role/d/{namespace}/{name}': {
+ get: {
+ summary: 'Ansible Role',
+ parameters: pathParams(
+ { name: 'namespace', example: 'openwisp' },
+ { name: 'name', example: 'openwisp2' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'role downloads' }
- }
+ static defaultBadgeData = { label: 'role downloads' }
- static render({ downloads }) {
- return {
- message: metric(downloads),
- color: downloadCount(downloads),
- }
- }
-
- async handle({ roleId }) {
- const json = await this.fetch({ roleId })
- return this.constructor.render({ downloads: json.download_count })
- }
-}
-
-class AnsibleGalaxyRoleName extends AnsibleGalaxyRole {
- static get category() {
- return 'other'
+ async fetch({ namespace, name }) {
+ const url = 'https://galaxy.ansible.com/api/v1/roles/'
+ return this._requestJson({
+ url,
+ schema: ansibleRoleSchema,
+ options: { searchParams: { namespace, name, limit: 1 } },
+ })
}
- static get route() {
- return {
- base: 'ansible/role',
- pattern: ':roleId',
+ async handle({ namespace, name }) {
+ const json = await this.fetch({ namespace, name })
+ if (json.results.length === 0) {
+ throw new NotFound({ prettyMessage: 'not found' })
}
+ return renderDownloadsBadge({ downloads: json.results[0].download_count })
}
-
- static get examples() {
- return [
- {
- title: 'Ansible Role',
- namedParams: { roleId: '3078' },
- staticPreview: this.render({
- name: 'ansible-roles.sublimetext3_packagecontrol',
- }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'role' }
- }
-
- static render({ name }) {
- return { message: name, color: 'blue' }
- }
-
- async handle({ roleId }) {
- const json = await this.fetch({ roleId })
- const name = `${json.summary_fields.namespace.name}.${json.name}`
- return this.constructor.render({ name })
- }
-}
-
-module.exports = {
- AnsibleGalaxyRoleDownloads,
- AnsibleGalaxyRoleName,
}
diff --git a/services/ansible/ansible-role.tester.js b/services/ansible/ansible-role.tester.js
index ac1a3980d4585..01bf24576ab25 100644
--- a/services/ansible/ansible-role.tester.js
+++ b/services/ansible/ansible-role.tester.js
@@ -1,26 +1,15 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { isMetric } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
- id: 'AnsibleRole',
- title: 'AnsibleRole',
+import { isMetric } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
+ id: 'AnsibleGalaxyRole',
+ title: 'AnsibleGalaxyRole',
pathPrefix: '/ansible/role',
-}))
-
-t.create('role name (valid)')
- .get('/14542.json')
- .expectBadge({ label: 'role', message: 'openwisp.openwisp2' })
-
-t.create('role name (not found)')
- .get('/000.json')
- .expectBadge({ label: 'role', message: 'not found' })
+})
t.create('role downloads (valid)')
- .get('/d/14542.json')
+ .get('/d/openwisp/openwisp2.json')
.expectBadge({ label: 'role downloads', message: isMetric })
t.create('role downloads (not found)')
- .get('/d/does-not-exist.json')
+ .get('/d/does-not/exist.json')
.expectBadge({ label: 'role downloads', message: 'not found' })
diff --git a/services/apm/apm.service.js b/services/apm/apm.service.js
deleted file mode 100644
index c1db39ecee5e6..0000000000000
--- a/services/apm/apm.service.js
+++ /dev/null
@@ -1,159 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const { renderVersionBadge } = require('../version')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, InvalidResponse } = require('..')
-
-const keywords = ['atom']
-
-const schema = Joi.object({
- downloads: nonNegativeInteger,
- releases: Joi.object({
- latest: Joi.string().required(),
- }),
- metadata: Joi.object({
- license: Joi.string().required(),
- }),
-})
-
-class BaseAPMService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'apm' }
- }
-
- async fetch({ packageName }) {
- return this._requestJson({
- schema,
- url: `https://atom.io/api/packages/${packageName}`,
- errorMessages: { 404: 'package not found' },
- })
- }
-}
-
-class APMDownloads extends BaseAPMService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'apm/dm',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'APM',
- namedParams: { packageName: 'vim-mode' },
- staticPreview: this.render({ downloads: '60043' }),
- keywords,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
-
- static render({ downloads }) {
- return { message: metric(downloads), color: 'green' }
- }
-
- async handle({ packageName }) {
- const json = await this.fetch({ packageName })
- return this.constructor.render({ downloads: json.downloads })
- }
-}
-
-class APMVersion extends BaseAPMService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'apm/v',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'APM',
- namedParams: { packageName: 'vim-mode' },
- staticPreview: this.render({ version: '0.6.0' }),
- keywords,
- },
- ]
- }
-
- static render({ version }) {
- return renderVersionBadge({ version })
- }
-
- async handle({ packageName }) {
- const json = await this.fetch({ packageName })
-
- const version = json.releases.latest
- if (!version)
- throw new InvalidResponse({
- underlyingError: new Error('version is invalid'),
- })
- return this.constructor.render({ version })
- }
-}
-
-class APMLicense extends BaseAPMService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'apm/l',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'APM',
- namedParams: { packageName: 'vim-mode' },
- staticPreview: this.render({ license: 'MIT' }),
- keywords,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'license' }
- }
-
- static render({ license }) {
- return renderLicenseBadge({ license })
- }
-
- async handle({ packageName }) {
- const json = await this.fetch({ packageName })
-
- const license = json.metadata.license
- if (!license)
- throw new InvalidResponse({
- underlyingError: new Error('licence is invalid'),
- })
- return this.constructor.render({ license })
- }
-}
-
-module.exports = {
- APMDownloads,
- APMVersion,
- APMLicense,
-}
diff --git a/services/apm/apm.tester.js b/services/apm/apm.tester.js
deleted file mode 100644
index de046039a906b..0000000000000
--- a/services/apm/apm.tester.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { invalidJSON } = require('../response-fixtures')
-const { isMetric, isVPlusTripleDottedVersion } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
- id: 'apm',
- title: 'Atom Package Manager',
-}))
-
-t.create('Downloads')
- .get('/dm/vim-mode.json')
- .expectBadge({ label: 'downloads', message: isMetric })
-
-t.create('Version')
- .get('/v/vim-mode.json')
- .expectBadge({ label: 'apm', message: isVPlusTripleDottedVersion })
-
-t.create('License')
- .get('/l/vim-mode.json')
- .expectBadge({ label: 'license', message: 'MIT' })
-
-t.create('Downloads | Package not found')
- .get('/dm/notapackage.json')
- .expectBadge({ label: 'downloads', message: 'package not found' })
-
-t.create('Version | Package not found')
- .get('/v/notapackage.json')
- .expectBadge({ label: 'apm', message: 'package not found' })
-
-t.create('License | Package not found')
- .get('/l/notapackage.json')
- .expectBadge({ label: 'license', message: 'package not found' })
-
-t.create('Invalid version')
- .get('/dm/vim-mode.json')
- .intercept(nock =>
- nock('https://atom.io')
- .get('/api/packages/vim-mode')
- .reply(200, '{"releases":{}}')
- )
- .expectBadge({ label: 'downloads', message: 'invalid response data' })
-
-t.create('Invalid License')
- .get('/l/vim-mode.json')
- .intercept(nock =>
- nock('https://atom.io')
- .get('/api/packages/vim-mode')
- .reply(200, '{"metadata":{}}')
- )
- .expectBadge({ label: 'license', message: 'invalid response data' })
-
-t.create('Unexpected response')
- .get('/dm/vim-mode.json')
- .intercept(nock =>
- nock('https://atom.io')
- .get('/api/packages/vim-mode')
- .reply(invalidJSON)
- )
- .expectBadge({ label: 'downloads', message: 'unparseable json response' })
diff --git a/services/appveyor/appveyor-base.js b/services/appveyor/appveyor-base.js
index e97d3a38a5be4..9903beae5fafa 100644
--- a/services/appveyor/appveyor-base.js
+++ b/services/appveyor/appveyor-base.js
@@ -1,18 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { isBuildStatus } = require('../build-status')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { isBuildStatus } from '../build-status.js'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
build: Joi.object({
status: isBuildStatus,
jobs: Joi.array()
.items({
- name: Joi.string()
- .allow('')
- .required(),
+ name: Joi.string().allow('').required(),
status: isBuildStatus,
testsCount: nonNegativeInteger,
passedTestsCount: nonNegativeInteger,
@@ -22,10 +18,8 @@ const schema = Joi.object({
}),
}).required()
-module.exports = class AppVeyorBase extends BaseJsonService {
- static get category() {
- return 'build'
- }
+export default class AppVeyorBase extends BaseJsonService {
+ static category = 'build'
async fetch({ user, repo, branch }) {
let url = `https://ci.appveyor.com/api/projects/${user}/${repo}`
@@ -35,7 +29,7 @@ module.exports = class AppVeyorBase extends BaseJsonService {
return this._requestJson({
schema,
url,
- errorMessages: { 404: 'project not found or access denied' },
+ httpErrors: { 404: 'project not found or access denied' },
})
}
diff --git a/services/appveyor/appveyor-build-redirect.service.js b/services/appveyor/appveyor-build-redirect.service.js
index df723459997c0..61e43b31b2ccb 100644
--- a/services/appveyor/appveyor-build-redirect.service.js
+++ b/services/appveyor/appveyor-build-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
redirector({
category: 'build',
route: {
diff --git a/services/appveyor/appveyor-build-redirect.tester.js b/services/appveyor/appveyor-build-redirect.tester.js
index 625a1f1a169e8..2a9916c6b4aa0 100644
--- a/services/appveyor/appveyor-build-redirect.tester.js
+++ b/services/appveyor/appveyor-build-redirect.tester.js
@@ -1,23 +1,15 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'AppveyorBuildRedirect',
title: 'AppveyorBuildRedirect',
pathPrefix: '/appveyor/ci',
-}))
+})
t.create('Appveyor CI')
- .get('/gruntjs/grunt', {
- followRedirect: false,
- })
- .expectStatus(301)
- .expectHeader('Location', '/appveyor/build/gruntjs/grunt.svg')
+ .get('/gruntjs/grunt')
+ .expectRedirect('/appveyor/build/gruntjs/grunt.svg')
t.create('Appveyor CI (branch)')
- .get('/gruntjs/grunt/develop', {
- followRedirect: false,
- })
- .expectStatus(301)
- .expectHeader('Location', '/appveyor/build/gruntjs/grunt/develop.svg')
+ .get('/gruntjs/grunt/develop')
+ .expectRedirect('/appveyor/build/gruntjs/grunt/develop.svg')
diff --git a/services/appveyor/appveyor-build.service.js b/services/appveyor/appveyor-build.service.js
index e8b7118eda38b..cc71150448669 100644
--- a/services/appveyor/appveyor-build.service.js
+++ b/services/appveyor/appveyor-build.service.js
@@ -1,28 +1,30 @@
-'use strict'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { pathParams } from '../index.js'
+import AppVeyorBase from './appveyor-base.js'
-const { renderBuildStatusBadge } = require('../build-status')
-const AppVeyorBase = require('./appveyor-base')
+export default class AppVeyorBuild extends AppVeyorBase {
+ static route = this.buildRoute('appveyor/build')
-module.exports = class AppVeyorBuild extends AppVeyorBase {
- static get route() {
- return this.buildRoute('appveyor/build')
- }
-
- static get examples() {
- return [
- {
- title: 'AppVeyor',
- pattern: ':user/:repo',
- namedParams: { user: 'gruntjs', repo: 'grunt' },
- staticPreview: this.render({ status: 'success' }),
+ static openApi = {
+ '/appveyor/build/{user}/{repo}': {
+ get: {
+ summary: 'AppVeyor Build',
+ parameters: pathParams(
+ { name: 'user', example: 'gruntjs' },
+ { name: 'repo', example: 'grunt' },
+ ),
},
- {
- title: 'AppVeyor branch',
- pattern: ':user/:repo/:branch',
- namedParams: { user: 'gruntjs', repo: 'grunt', branch: 'master' },
- staticPreview: this.render({ status: 'success' }),
+ },
+ '/appveyor/build/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'AppVeyor Build (with branch)',
+ parameters: pathParams(
+ { name: 'user', example: 'gruntjs' },
+ { name: 'repo', example: 'grunt' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
static render({ status }) {
diff --git a/services/appveyor/appveyor-build.tester.js b/services/appveyor/appveyor-build.tester.js
index 9858a91f17385..420dd9660580a 100644
--- a/services/appveyor/appveyor-build.tester.js
+++ b/services/appveyor/appveyor-build.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('CI status')
.timeout(10000)
@@ -26,7 +25,7 @@ t.create('CI status on project that does exist but has no builds yet')
.intercept(nock =>
nock('https://ci.appveyor.com/api/projects/')
.get('/gruntjs/grunt')
- .reply(200, {})
+ .reply(200, {}),
)
.expectBadge({
label: 'build',
diff --git a/services/appveyor/appveyor-job-build.service.js b/services/appveyor/appveyor-job-build.service.js
index b83f6ea132ca8..59f5278ec8c38 100644
--- a/services/appveyor/appveyor-job-build.service.js
+++ b/services/appveyor/appveyor-job-build.service.js
@@ -1,41 +1,35 @@
-'use strict'
-
-const { renderBuildStatusBadge } = require('../build-status')
-const { NotFound } = require('..')
-const AppVeyorBase = require('./appveyor-base')
-
-module.exports = class AppVeyorJobBuild extends AppVeyorBase {
- static get route() {
- return {
- base: 'appveyor/job/build',
- pattern: ':user/:repo/:job/:branch*',
- }
+import { renderBuildStatusBadge } from '../build-status.js'
+import { NotFound, pathParams } from '../index.js'
+import AppVeyorBase from './appveyor-base.js'
+
+export default class AppVeyorJobBuild extends AppVeyorBase {
+ static route = {
+ base: 'appveyor/job/build',
+ pattern: ':user/:repo/:job/:branch*',
}
- static get examples() {
- return [
- {
- title: 'AppVeyor Job',
- pattern: ':user/:repo/:job',
- namedParams: {
- user: 'wpmgprostotema',
- repo: 'voicetranscoder',
- job: 'Linux',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
+ static openApi = {
+ '/appveyor/job/build/{user}/{repo}/{job}': {
+ get: {
+ summary: 'AppVeyor Job',
+ parameters: pathParams(
+ { name: 'user', example: 'wpmgprostotema' },
+ { name: 'repo', example: 'voicetranscoder' },
+ { name: 'job', example: 'Linux' },
+ ),
},
- {
- title: 'AppVeyor Job branch',
- pattern: ':user/:repo/:job/:branch',
- namedParams: {
- user: 'wpmgprostotema',
- repo: 'voicetranscoder',
- job: 'Windows',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
+ },
+ '/appveyor/job/build/{user}/{repo}/{job}/{branch}': {
+ get: {
+ summary: 'AppVeyor Job (with branch)',
+ parameters: pathParams(
+ { name: 'user', example: 'wpmgprostotema' },
+ { name: 'repo', example: 'voicetranscoder' },
+ { name: 'job', example: 'Windows' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
transform({ data, jobName }) {
diff --git a/services/appveyor/appveyor-job-build.spec.js b/services/appveyor/appveyor-job-build.spec.js
index 6422549aec48a..494ff9edca2dc 100644
--- a/services/appveyor/appveyor-job-build.spec.js
+++ b/services/appveyor/appveyor-job-build.spec.js
@@ -1,11 +1,9 @@
-'use strict'
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { NotFound } from '../index.js'
+import AppveyorJobBuild from './appveyor-job-build.service.js'
-const { expect } = require('chai')
-const { test, given } = require('sazerac')
-const { NotFound } = require('..')
-const AppveyorJobBuild = require('./appveyor-job-build.service')
-
-describe('AppveyorJobBuild', function() {
+describe('AppveyorJobBuild', function () {
test(AppveyorJobBuild.prototype.transform, () => {
given({ data: {} }).expect({
status: 'no builds found',
@@ -31,13 +29,13 @@ describe('AppveyorJobBuild', function() {
})
})
- it('throws NotFound when response is missing jobs', function() {
+ it('throws NotFound when response is missing jobs', function () {
expect(() => AppveyorJobBuild.prototype.transform({ data: { build: {} } }))
.to.throw(NotFound)
.with.property('prettyMessage', 'no jobs found')
})
- it('throws NotFound when specified job missing jobs', function() {
+ it('throws NotFound when specified job missing jobs', function () {
expect(() =>
AppveyorJobBuild.prototype.transform({
jobName: 'mac',
@@ -55,7 +53,7 @@ describe('AppveyorJobBuild', function() {
],
},
},
- })
+ }),
)
.to.throw(NotFound)
.with.property('prettyMessage', 'job not found')
diff --git a/services/appveyor/appveyor-job-build.tester.js b/services/appveyor/appveyor-job-build.tester.js
index 81cccb39f252a..a31306e30ec00 100644
--- a/services/appveyor/appveyor-job-build.tester.js
+++ b/services/appveyor/appveyor-job-build.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Job CI status')
.timeout(10000)
diff --git a/services/appveyor/appveyor-tests.service.js b/services/appveyor/appveyor-tests.service.js
index 803dac6c6270d..2bef4b4cc0798 100644
--- a/services/appveyor/appveyor-tests.service.js
+++ b/services/appveyor/appveyor-tests.service.js
@@ -1,113 +1,51 @@
-'use strict'
-
-const {
+import {
testResultQueryParamSchema,
+ testResultOpenApiQueryParams,
renderTestResultBadge,
-} = require('../test-results')
-const AppVeyorBase = require('./appveyor-base')
-
-const documentation = `
-
- You may change the "passed", "failed" and "skipped" text on this badge by supplying query parameters &passed_label=, &failed_label= and &skipped_label= respectively.
-
-
-
- For example, if you want to use a different terminology:
-
- /appveyor/tests/NZSmartie/coap-net-iu0to.svg?passed_label=good&failed_label=bad&skipped_label=n%2Fa
-
-
-
- Or symbols:
-
- /appveyor/tests/NZSmartie/coap-net-iu0to.svg?compact_message&passed_label=%F0%9F%8E%89&failed_label=%F0%9F%92%A2&skipped_label=%F0%9F%A4%B7
-
-
-
- There is also a &compact_message query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
-
-`
-
-const commonPreviewProps = {
- passed: 477,
- failed: 2,
- skipped: 0,
- total: 479,
- isCompact: false,
-}
+ documentation as description,
+} from '../test-results.js'
+import { pathParams } from '../index.js'
+import AppVeyorBase from './appveyor-base.js'
-module.exports = class AppVeyorTests extends AppVeyorBase {
- static get route() {
- return {
- ...this.buildRoute('appveyor/tests'),
- queryParamSchema: testResultQueryParamSchema,
- }
+export default class AppVeyorTests extends AppVeyorBase {
+ static category = 'test-results'
+ static route = {
+ ...this.buildRoute('appveyor/tests'),
+ queryParamSchema: testResultQueryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'AppVeyor tests',
- pattern: ':user/:repo',
- namedParams: {
- user: 'NZSmartie',
- repo: 'coap-net-iu0to',
- },
- staticPreview: this.render(commonPreviewProps),
- documentation,
- },
- {
- title: 'AppVeyor tests (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'NZSmartie',
- repo: 'coap-net-iu0to',
- branch: 'master',
- },
- staticPreview: this.render(commonPreviewProps),
- documentation,
+ static openApi = {
+ '/appveyor/tests/{user}/{repo}': {
+ get: {
+ summary: 'AppVeyor tests',
+ description,
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: 'NZSmartie' },
+ { name: 'repo', example: 'coap-net-iu0to' },
+ ),
+ ...testResultOpenApiQueryParams,
+ ],
},
- {
- title: 'AppVeyor tests (compact)',
- pattern: ':user/:repo',
- namedParams: {
- user: 'NZSmartie',
- repo: 'coap-net-iu0to',
- },
- queryParams: { compact_message: null },
- staticPreview: this.render({
- ...commonPreviewProps,
- isCompact: true,
- }),
- documentation,
+ },
+ '/appveyor/tests/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'AppVeyor tests (with branch)',
+ description,
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: 'NZSmartie' },
+ { name: 'repo', example: 'coap-net-iu0to' },
+ { name: 'branch', example: 'master' },
+ ),
+ ...testResultOpenApiQueryParams,
+ ],
},
- {
- title: 'AppVeyor tests with custom labels',
- pattern: ':user/:repo',
- namedParams: {
- user: 'NZSmartie',
- repo: 'coap-net-iu0to',
- },
- queryParams: {
- passed_label: 'good',
- failed_label: 'bad',
- skipped_label: 'n/a',
- },
- staticPreview: this.render({
- ...commonPreviewProps,
- passedLabel: 'good',
- failedLabel: 'bad',
- skippedLabel: 'n/a',
- }),
- documentation,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'tests',
- }
+ static defaultBadgeData = {
+ label: 'tests',
}
static render({
@@ -139,7 +77,7 @@ module.exports = class AppVeyorTests extends AppVeyorBase {
passed_label: passedLabel,
failed_label: failedLabel,
skipped_label: skippedLabel,
- }
+ },
) {
const isCompact = compactMessage !== undefined
const data = await this.fetch({ user, repo, branch })
diff --git a/services/appveyor/appveyor-tests.tester.js b/services/appveyor/appveyor-tests.tester.js
index a08bc2937ac33..8d7130aabe7b4 100644
--- a/services/appveyor/appveyor-tests.tester.js
+++ b/services/appveyor/appveyor-tests.tester.js
@@ -1,39 +1,26 @@
-'use strict'
-
-const queryString = require('querystring')
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isAppveyorTestTotals = Joi.string().regex(
- /^[0-9]+ passed(, [0-9]+ failed)?(, [0-9]+ skipped)?$/
-)
-
-const isCompactAppveyorTestTotals = Joi.string().regex(
- /^✔ [0-9]+( \| ✘ [0-9]+)?( \| ➟ [0-9]+)?$/
-)
-
-const isCustomAppveyorTestTotals = Joi.string().regex(
- /^[0-9]+ good(, [0-9]+ bad)?(, [0-9]+ n\/a)?$/
-)
-
-const isCompactCustomAppveyorTestTotals = Joi.string().regex(
- /^💃 [0-9]+( \| 🤦♀️ [0-9]+)?( \| 🤷 [0-9]+)?$/
-)
+import {
+ isDefaultTestTotals,
+ isDefaultCompactTestTotals,
+ isCustomTestTotals,
+ isCustomCompactTestTotals,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Test status')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to.json')
- .expectBadge({ label: 'tests', message: isAppveyorTestTotals })
+ .expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status on branch')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to/master.json')
- .expectBadge({ label: 'tests', message: isAppveyorTestTotals })
+ .expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status with compact message')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to.json?compact_message')
- .expectBadge({ label: 'tests', message: isCompactAppveyorTestTotals })
+ .expectBadge({ label: 'tests', message: isDefaultCompactTestTotals })
t.create('Test status with custom labels')
.timeout(10000)
@@ -44,21 +31,21 @@ t.create('Test status with custom labels')
skipped_label: 'n/a',
},
})
- .expectBadge({ label: 'tests', message: isCustomAppveyorTestTotals })
+ .expectBadge({ label: 'tests', message: isCustomTestTotals })
t.create('Test status with compact message and custom labels')
.timeout(10000)
- .get(
- `/NZSmartie/coap-net-iu0to.json?${queryString.stringify({
+ .get('/NZSmartie/coap-net-iu0to.json', {
+ qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦♀️',
skipped_label: '🤷',
- })}`
- )
+ },
+ })
.expectBadge({
label: 'tests',
- message: isCompactCustomAppveyorTestTotals,
+ message: isCustomCompactTestTotals,
})
t.create('Test status on non-existent project')
@@ -74,7 +61,7 @@ t.create('Test status on project that does exist but has no builds yet')
.intercept(nock =>
nock('https://ci.appveyor.com/api/projects/')
.get('/gruntjs/grunt')
- .reply(200, {})
+ .reply(200, {}),
)
.expectBadge({
label: 'tests',
diff --git a/services/archlinux/archlinux.service.js b/services/archlinux/archlinux.service.js
index 8f3b8d32575be..84a6c67fca020 100644
--- a/services/archlinux/archlinux.service.js
+++ b/services/archlinux/archlinux.service.js
@@ -1,50 +1,49 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
pkgver: Joi.string().required(),
}).required()
-module.exports = class ArchLinux extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'archlinux/v',
- pattern: ':repository/:architecture/:packageName',
- }
+export default class ArchLinux extends BaseJsonService {
+ static category = 'version'
+ static route = {
+ base: 'archlinux/v',
+ pattern: ':repository/:architecture/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Arch Linux package',
- namedParams: {
- architecture: 'x86_64',
- repository: 'core',
- packageName: 'pacman',
- },
- staticPreview: renderVersionBadge({ version: '5.1.3' }),
+ static openApi = {
+ '/archlinux/v/{repository}/{architecture}/{packageName}': {
+ get: {
+ summary: 'Arch Linux package',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'core',
+ },
+ {
+ name: 'architecture',
+ example: 'x86_64',
+ },
+ {
+ name: 'packageName',
+ example: 'pacman',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'arch linux' }
- }
+ static defaultBadgeData = { label: 'arch linux' }
async handle({ repository, architecture, packageName }) {
const data = await this._requestJson({
schema,
url: `https://www.archlinux.org/packages/${encodeURIComponent(
- repository
+ repository,
)}/${encodeURIComponent(architecture)}/${encodeURIComponent(
- packageName
+ packageName,
)}/json/`,
})
return renderVersionBadge({ version: data.pkgver })
diff --git a/services/archlinux/archlinux.tester.js b/services/archlinux/archlinux.tester.js
index 379e8133c6f8d..4ad56d5ec9293 100644
--- a/services/archlinux/archlinux.tester.js
+++ b/services/archlinux/archlinux.tester.js
@@ -1,12 +1,9 @@
-'use strict'
-
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Arch Linux package (valid)')
- .get('/core/x86_64/pacman.json')
+ .get('/core/x86_64/iptables.json')
.expectBadge({
label: 'arch linux',
message: isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
@@ -21,7 +18,7 @@ t.create('Arch Linux package (valid)')
pkgname: 'pacman',
pkgver: '5.1.3',
pkgrel: '1',
- })
+ }),
)
.expectBadge({ label: 'arch linux', message: 'v5.1.3' })
diff --git a/services/aur/aur.service.js b/services/aur/aur.service.js
index e9bb63cab4579..6fb2f4137a59c 100644
--- a/services/aur/aur.service.js
+++ b/services/aur/aur.service.js
@@ -1,39 +1,36 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { addv, metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
-const { formatDate } = require('../text-formatters')
-const { age: ageColor } = require('../color-formatters')
-const { InvalidResponse } = require('..')
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { renderLicenseBadge } from '../licenses.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import {
+ BaseJsonService,
+ NotFound,
+ InvalidResponse,
+ pathParams,
+} from '../index.js'
+import { renderVersionBadge } from '../version.js'
const aurSchema = Joi.object({
resultcount: nonNegativeInteger,
- results: Joi.alternatives(
- Joi.array()
- .length(0)
- .required(),
- Joi.object({
- License: Joi.string()
- .required()
- .allow(null),
- NumVotes: nonNegativeInteger,
- Version: Joi.string().required(),
- OutOfDate: nonNegativeInteger.allow(null),
- Maintainer: Joi.string()
- .required()
- .allow(null),
- LastModified: nonNegativeInteger,
- }).required()
- ),
+ results: Joi.array()
+ .items(
+ Joi.object({
+ License: Joi.array().items(Joi.string()).allow(null),
+ NumVotes: nonNegativeInteger,
+ Popularity: Joi.number().precision(2).min(0).required(),
+ Version: Joi.string().required(),
+ OutOfDate: nonNegativeInteger.allow(null),
+ Maintainer: Joi.string().required().allow(null),
+ LastModified: nonNegativeInteger,
+ }),
+ )
+ .required(),
}).required()
class BaseAurService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'aur' }
- }
+ static defaultBadgeData = { label: 'aur' }
static _validate(data, schema) {
if (data.resultcount === 0) {
@@ -50,83 +47,63 @@ class BaseAurService extends BaseJsonService {
// https://wiki.archlinux.org/index.php/Aurweb_RPC_interface
return this._requestJson({
schema: aurSchema,
- url: 'https://aur.archlinux.org/rpc.php',
- options: { qs: { type: 'info', arg: packageName } },
+ url: 'https://aur.archlinux.org/rpc',
+ options: { searchParams: { v: 5, type: 'info', arg: packageName } },
})
}
}
class AurLicense extends BaseAurService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'aur/license',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'AUR license',
- namedParams: { packageName: 'android-studio' },
- staticPreview: this.render({ license: 'Apache' }),
+ static category = 'license'
+ static route = { base: 'aur/license', pattern: ':packageName' }
+
+ static openApi = {
+ '/aur/license/{packageName}': {
+ get: {
+ summary: 'AUR License',
+ description: 'Arch linux User Repository License',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'android-studio',
+ }),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'license' }
+ },
}
- static render({ license }) {
- return { message: license, color: 'blue' }
- }
+ static defaultBadgeData = { label: 'license' }
transform(json) {
- const license = json.results.License
- if (!license) {
+ const licenses = json.results[0].License
+ if (!licenses || licenses.length === 0) {
throw new NotFound({ prettyMessage: 'not specified' })
}
- return { license }
+ return { licenses }
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
- const { license } = this.transform(json)
- return this.constructor.render({ license })
+ const { licenses } = this.transform(json)
+ return renderLicenseBadge({ licenses, color: 'blue' })
}
}
class AurVotes extends BaseAurService {
- static get category() {
- return 'rating'
- }
+ static category = 'rating'
- static get route() {
- return {
- base: 'aur/votes',
- pattern: ':packageName',
- }
- }
+ static route = { base: 'aur/votes', pattern: ':packageName' }
- static get examples() {
- return [
- {
- title: 'AUR votes',
- namedParams: { packageName: 'dropbox' },
- staticPreview: this.render({ votes: '2280' }),
+ static openApi = {
+ '/aur/votes/{packageName}': {
+ get: {
+ summary: 'AUR Votes',
+ description: 'Arch linux User Repository Votes',
+ parameters: pathParams({ name: 'packageName', example: 'dropbox' }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'votes' }
- }
+ static defaultBadgeData = { label: 'votes' }
static render({ votes }) {
return {
@@ -137,71 +114,94 @@ class AurVotes extends BaseAurService {
async handle({ packageName }) {
const json = await this.fetch({ packageName })
- return this.constructor.render({ votes: json.results.NumVotes })
+ return this.constructor.render({ votes: json.results[0].NumVotes })
}
}
-class AurVersion extends BaseAurService {
- static get category() {
- return 'version'
+class AurPopularity extends BaseAurService {
+ static category = 'rating'
+
+ static route = { base: 'aur/popularity', pattern: ':packageName' }
+
+ static openApi = {
+ '/aur/popularity/{packageName}': {
+ get: {
+ summary: 'AUR Popularity',
+ description: 'Arch linux User Repository Popularity',
+ parameters: pathParams({ name: 'packageName', example: 'dropbox' }),
+ },
+ },
}
- static get route() {
+ static defaultBadgeData = { label: 'popularity' }
+
+ static render({ popularity }) {
return {
- base: 'aur/version',
- pattern: ':packageName',
+ message: popularity,
+ color: floorCountColor(popularity, 0.5, 2.5, 5),
}
}
- static get examples() {
- return [
- {
- title: 'AUR version',
- namedParams: { packageName: 'visual-studio-code-bin' },
- staticPreview: this.render({ version: '1.34.0-2', outOfDate: null }),
+ async handle({ packageName }) {
+ const json = await this.fetch({ packageName })
+ return this.constructor.render({ popularity: json.results[0].Popularity })
+ }
+}
+
+class AurVersion extends BaseAurService {
+ static category = 'version'
+ static route = { base: 'aur/version', pattern: ':packageName' }
+
+ static openApi = {
+ '/aur/version/{packageName}': {
+ get: {
+ summary: 'AUR Version',
+ description: 'Arch linux User Repository Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'visual-studio-code-bin',
+ }),
},
- ]
+ },
}
+ static _cacheLength = 3600
+
static render({ version, outOfDate }) {
const color = outOfDate === null ? 'blue' : 'orange'
- return { message: addv(version), color }
+ return renderVersionBadge({ version, versionFormatter: () => color })
}
async handle({ packageName }) {
- const json = await this.fetch({ packageName })
+ const {
+ results: [{ Version: version, OutOfDate: outOfDate }],
+ } = await this.fetch({ packageName })
return this.constructor.render({
- version: json.results.Version,
- outOfDate: json.results.OutOfDate,
+ version,
+ outOfDate,
})
}
}
class AurMaintainer extends BaseAurService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'aur/maintainer',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'AUR maintainer',
- namedParams: { packageName: 'google-chrome' },
- staticPreview: this.render({ maintainer: 'luzifer' }),
+ static category = 'other'
+
+ static route = { base: 'aur/maintainer', pattern: ':packageName' }
+
+ static openApi = {
+ '/aur/maintainer/{packageName}': {
+ get: {
+ summary: 'AUR Maintainer',
+ description: 'Arch linux User Repository Maintainer',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'google-chrome',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'maintainer', color: 'blue' }
- }
+ static defaultBadgeData = { label: 'maintainer', color: 'blue' }
static render({ maintainer }) {
return { message: maintainer }
@@ -209,7 +209,7 @@ class AurMaintainer extends BaseAurService {
async handle({ packageName }) {
const {
- results: { Maintainer: maintainer },
+ results: [{ Maintainer: maintainer }],
} = await this.fetch({ packageName })
if (!maintainer) {
throw new InvalidResponse({ prettyMessage: 'No maintainer' })
@@ -219,48 +219,40 @@ class AurMaintainer extends BaseAurService {
}
class AurLastModified extends BaseAurService {
- static get category() {
- return 'activity'
- }
-
- static get route() {
- return {
- base: 'aur/last-modified',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'AUR last modified',
- namedParams: { packageName: 'google-chrome' },
- staticPreview: this.render({ date: new Date().getTime() }),
+ static category = 'activity'
+
+ static route = {
+ base: 'aur/last-modified',
+ pattern: ':packageName',
+ }
+
+ static openApi = {
+ '/aur/last-modified/{packageName}': {
+ get: {
+ summary: 'AUR Last Modified',
+ description: 'Arch linux User Repository Last Modified',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'google-chrome',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'last modified' }
- }
-
- static render({ date }) {
- const color = ageColor(date)
- const message = formatDate(date)
- return { color, message }
- }
+ static defaultBadgeData = { label: 'last modified' }
async handle({ packageName }) {
const json = await this.fetch({ packageName })
- const date = 1000 * parseInt(json.results.LastModified)
- return this.constructor.render({ date })
+ const date = 1000 * parseInt(json.results[0].LastModified)
+ return renderDateBadge(date)
}
}
-module.exports = {
+export {
AurLicense,
AurVersion,
AurVotes,
+ AurPopularity,
AurMaintainer,
AurLastModified,
}
diff --git a/services/aur/aur.spec.js b/services/aur/aur.spec.js
new file mode 100644
index 0000000000000..a707fa7d5f1c2
--- /dev/null
+++ b/services/aur/aur.spec.js
@@ -0,0 +1,18 @@
+import { test, given } from 'sazerac'
+import { AurVersion } from './aur.service.js'
+
+describe('AurVersion', function () {
+ test(AurVersion.render, () => {
+ given({ version: '1:1.1.42.622-1', outOfDate: 1 }).expect({
+ color: 'orange',
+ message: 'v1:1.1.42.622-1',
+ label: undefined,
+ })
+
+ given({ version: '7', outOfDate: null }).expect({
+ color: 'blue',
+ message: 'v7',
+ label: undefined,
+ })
+ })
+})
diff --git a/services/aur/aur.tester.js b/services/aur/aur.tester.js
index f725d64b133f9..f6db2f9944007 100644
--- a/services/aur/aur.tester.js
+++ b/services/aur/aur.tester.js
@@ -1,16 +1,15 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const {
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
isVPlusDottedVersionNClausesWithOptionalSuffix,
isMetric,
-} = require('../test-validators')
-const { isFormattedDate } = require('../test-validators')
+ isFormattedDate,
+} from '../test-validators.js'
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'aur',
title: 'Arch Linux AUR',
-}))
+})
// version tests
@@ -19,15 +18,7 @@ t.create('version (valid)')
.expectBadge({
label: 'aur',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
- color: 'blue',
- })
-
-t.create('version (valid, out of date)')
- .get('/version/gog-gemini-rue.json')
- .expectBadge({
- label: 'aur',
- message: isVPlusDottedVersionNClausesWithOptionalSuffix,
- color: 'orange',
+ color: Joi.string().valid('blue', 'orange').required(),
})
t.create('version (not found)')
@@ -36,17 +27,28 @@ t.create('version (not found)')
// votes tests
-t.create('votes (valid)')
- .get('/votes/google-chrome.json')
- .expectBadge({
- label: 'votes',
- message: isMetric,
- })
+t.create('votes (valid)').get('/votes/google-chrome.json').expectBadge({
+ label: 'votes',
+ message: isMetric,
+})
t.create('votes (not found)')
.get('/votes/not-a-package.json')
.expectBadge({ label: 'votes', message: 'package not found' })
+// popularity tests
+
+t.create('popularity (valid)')
+ .get('/popularity/google-chrome.json')
+ .expectBadge({
+ label: 'popularity',
+ message: Joi.number().precision(2).required(),
+ })
+
+t.create('popularity (not found)')
+ .get('/popularity/not-a-package.json')
+ .expectBadge({ label: 'popularity', message: 'package not found' })
+
// license tests
t.create('license (valid)')
@@ -54,7 +56,57 @@ t.create('license (valid)')
.expectBadge({ label: 'license', message: 'MIT' })
t.create('license (no license)')
- .get('/license/dns-zone-blacklist-git.json')
+ .get('/license/vscodium-bin.json')
+ .intercept(nock =>
+ nock('https://aur.archlinux.org')
+ .get('/rpc')
+ .query({
+ v: 5,
+ type: 'info',
+ arg: 'vscodium-bin',
+ })
+ .reply(200, {
+ resultcount: 1,
+ results: [
+ {
+ License: null,
+ NumVotes: 1,
+ Popularity: 0,
+ Version: '1',
+ OutOfDate: null,
+ Maintainer: null,
+ LastModified: 1,
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'license', message: 'not specified' })
+
+t.create('license (empty license)')
+ .get('/license/vscodium-bin.json')
+ .intercept(nock =>
+ nock('https://aur.archlinux.org')
+ .get('/rpc')
+ .query({
+ v: 5,
+ type: 'info',
+ arg: 'vscodium-bin',
+ })
+ .reply(200, {
+ resultcount: 1,
+ results: [
+ {
+ License: [],
+ NumVotes: 1,
+ Popularity: 0,
+ Version: '1',
+ OutOfDate: null,
+ Maintainer: null,
+ LastModified: 1,
+ },
+ ],
+ }),
+ )
.expectBadge({ label: 'license', message: 'not specified' })
t.create('license (package not found)')
@@ -65,7 +117,7 @@ t.create('license (package not found)')
t.create('maintainer (valid)')
.get('/maintainer/google-chrome.json')
- .expectBadge({ label: 'maintainer', message: 'luzifer' })
+ .expectBadge({ label: 'maintainer', message: 'gromit' })
t.create('maintainer (not found)')
.get('/maintainer/not-a-package.json')
diff --git a/services/azure-devops/azure-devops-base.js b/services/azure-devops/azure-devops-base.js
index 904db4c28bea7..04b618580527e 100644
--- a/services/azure-devops/azure-devops-base.js
+++ b/services/azure-devops/azure-devops-base.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, NotFound } from '../index.js'
const latestBuildSchema = Joi.object({
count: Joi.number().required(),
@@ -9,28 +7,26 @@ const latestBuildSchema = Joi.object({
.items(
Joi.object({
id: Joi.number().required(),
- })
+ }),
)
.required(),
}).required()
-module.exports = class AzureDevOpsBase extends BaseJsonService {
- static get auth() {
- return {
- passKey: 'azure_devops_token',
- authorizedOrigins: ['https://dev.azure.com'],
- defaultToEmptyStringForUser: true,
- }
+export default class AzureDevOpsBase extends BaseJsonService {
+ static auth = {
+ passKey: 'azure_devops_token',
+ authorizedOrigins: ['https://dev.azure.com'],
+ defaultToEmptyStringForUser: true,
}
- async fetch({ url, options, schema, errorMessages }) {
+ async fetch({ url, options, schema, httpErrors }) {
return this._requestJson(
this.authHelper.withBasicAuth({
schema,
url,
options,
- errorMessages,
- })
+ httpErrors,
+ }),
)
}
@@ -39,12 +35,12 @@ module.exports = class AzureDevOpsBase extends BaseJsonService {
project,
definitionId,
branch,
- errorMessages
+ httpErrors,
) {
// Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list?view=azure-devops-rest-5.0
const url = `https://dev.azure.com/${organization}/${project}/_apis/build/builds`
const options = {
- qs: {
+ searchParams: {
definitions: definitionId,
$top: 1,
statusFilter: 'completed',
@@ -53,14 +49,14 @@ module.exports = class AzureDevOpsBase extends BaseJsonService {
}
if (branch) {
- options.qs.branchName = `refs/heads/${branch}`
+ options.searchParams.branchName = `refs/heads/${branch}`
}
const json = await this.fetch({
url,
options,
schema: latestBuildSchema,
- errorMessages,
+ httpErrors,
})
if (json.count !== 1) {
diff --git a/services/azure-devops/azure-devops-build.service.js b/services/azure-devops/azure-devops-build.service.js
index 018d1032f094c..06d2cbf02566c 100644
--- a/services/azure-devops/azure-devops-build.service.js
+++ b/services/azure-devops/azure-devops-build.service.js
@@ -1,132 +1,226 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService, NotFound } = require('..')
-const { keywords, fetch } = require('./azure-devops-helpers')
+import Joi from 'joi'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { NotFound, queryParam, pathParam } from '../index.js'
+import AzureDevOpsBase from './azure-devops-base.js'
const queryParamSchema = Joi.object({
stage: Joi.string(),
job: Joi.string(),
})
-const documentation = `
-
- A badge requires three pieces of information: ORGANIZATION,
- PROJECT_ID and DEFINITION_ID.
-
-
- To start, edit your build definition and look at the url:
-
+const buildSchema = Joi.object({
+ count: Joi.number().required(),
+ value: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.number().required(),
+ status: Joi.string().required(),
+ result: Joi.string().allow(null),
+ }),
+ )
+ .required(),
+}).required()
+
+const timelineSchema = Joi.object({
+ records: Joi.array()
+ .items(
+ Joi.object({
+ type: Joi.string().required(),
+ name: Joi.string().required(),
+ result: Joi.string().allow(null),
+ }),
+ )
+ .required(),
+}).required()
+
+const description = `
+[Azure Devops](https://dev.azure.com/) (formerly VSO, VSTS) is Microsoft Azure's CI/CD platform.
+
+A badge requires three pieces of information:
+\`ORGANIZATION\`, \`PROJECT_ID\` and \`DEFINITION_ID\`.
+
+To start, edit your build definition and look at the url:
+
-
- Then use the Azure DevOps REST API to translate the
- PROJECT_NAME to a PROJECT_ID.
-
-
- Navigate to https://dev.azure.com/ORGANIZATION/_apis/projects/PROJECT_NAME
-
+
+Then use the Azure DevOps REST API to translate the
+\`PROJECT_NAME\` to a \`PROJECT_ID\`.
+
+Navigate to \`https://dev.azure.com/ORGANIZATION/_apis/projects/PROJECT_NAME\`
+
`
-module.exports = class AzureDevOpsBuild extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
+export default class AzureDevOpsBuild extends AzureDevOpsBase {
+ static category = 'build'
- static get route() {
- return {
- base: 'azure-devops/build',
- pattern: ':organization/:projectId/:definitionId/:branch*',
- queryParamSchema,
- }
+ static route = {
+ base: 'azure-devops/build',
+ pattern: ':organization/:projectId/:definitionId/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Azure DevOps builds',
- pattern: ':organization/:projectId/:definitionId',
- namedParams: {
- organization: 'totodem',
- projectId: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
- definitionId: '2',
- },
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
- },
- {
- title: 'Azure DevOps builds (branch)',
- pattern: ':organization/:projectId/:definitionId/:branch',
- namedParams: {
- organization: 'totodem',
- projectId: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
- definitionId: '2',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
+ static openApi = {
+ '/azure-devops/build/{organization}/{projectId}/{definitionId}': {
+ get: {
+ summary: 'Azure DevOps builds',
+ description,
+ parameters: [
+ pathParam({
+ name: 'organization',
+ example: 'totodem',
+ }),
+ pathParam({
+ name: 'projectId',
+ example: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
+ }),
+ pathParam({
+ name: 'definitionId',
+ example: '2',
+ }),
+ queryParam({
+ name: 'stage',
+ example: 'Successful Stage',
+ }),
+ queryParam({
+ name: 'job',
+ example: 'Successful Job',
+ }),
+ ],
},
- {
- title: 'Azure DevOps builds (stage)',
- namedParams: {
- organization: 'totodem',
- projectId: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
- definitionId: '5',
- },
- queryParams: {
- stage: 'Successful Stage',
- },
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
+ },
+ '/azure-devops/build/{organization}/{projectId}/{definitionId}/{branch}': {
+ get: {
+ summary: 'Azure DevOps builds (branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'organization',
+ example: 'totodem',
+ }),
+ pathParam({
+ name: 'projectId',
+ example: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
+ }),
+ pathParam({
+ name: 'definitionId',
+ example: '2',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ queryParam({
+ name: 'stage',
+ example: 'Successful Stage',
+ }),
+ queryParam({
+ name: 'job',
+ example: 'Successful Job',
+ }),
+ ],
},
- {
- title: 'Azure DevOps builds (job)',
- namedParams: {
- organization: 'totodem',
- projectId: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
- definitionId: '5',
- },
- queryParams: {
- stage: 'Successful Stage',
- job: 'Successful Job',
- },
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
- },
- ]
+ },
+ }
+
+ static defaultBadgeData = { label: 'build' }
+
+ // Map Azure DevOps `result` values (both build-level and timeline
+ // stage/job-level) onto the status vocabulary understood by
+ // renderBuildStatusBadge (services/build-status.js).
+ static resultMap = {
+ // build-level results (builds list)
+ canceled: 'canceled',
+ failed: 'failed',
+ none: 'no builds',
+ partiallySucceeded: 'partially succeeded',
+ succeeded: 'succeeded',
+ // timeline record-level results (stage / job)
+ abandoned: 'canceled',
+ skipped: 'skipped',
+ succeededWithIssues: 'partially succeeded',
+ }
+
+ // Look up the result of a single stage or job within a build via the
+ // Timeline API. A job takes precedence over a stage when both are given.
+ // Note: the Timeline endpoint does not accept the api-version used by the
+ // other Azure DevOps build endpoints.
+ async getStageOrJobResult(
+ organization,
+ projectId,
+ buildId,
+ stage,
+ job,
+ httpErrors,
+ ) {
+ const url = `https://dev.azure.com/${organization}/${projectId}/_apis/build/builds/${buildId}/timeline`
+ const { records } = await this.fetch({
+ url,
+ options: {},
+ schema: timelineSchema,
+ httpErrors,
+ })
+ const recordType = job ? 'Job' : 'Stage'
+ const recordName = job || stage
+ const record = records.find(
+ r => r.type === recordType && r.name === recordName,
+ )
+ if (!record) {
+ throw new NotFound({
+ prettyMessage: `${recordType.toLowerCase()} not found`,
+ })
+ }
+ return record.result
}
async handle(
{ organization, projectId, definitionId, branch },
- { stage, job }
+ { stage, job },
) {
- // Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/vsts/build/status/get
- const { status } = await fetch(this, {
- url: `https://dev.azure.com/${organization}/${projectId}/_apis/build/status/${definitionId}`,
- qs: {
- branchName: branch,
- stageName: stage,
- jobName: job,
- },
- errorMessages: {
- 404: 'user or project not found',
+ const httpErrors = {
+ 404: 'build pipeline not found',
+ }
+ // Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list
+ const url = `https://dev.azure.com/${organization}/${projectId}/_apis/build/builds`
+ const options = {
+ searchParams: {
+ definitions: definitionId,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
},
- })
- if (status === 'set up now') {
- throw new NotFound({ prettyMessage: 'definition not found' })
}
- if (status === 'unknown') {
- throw new NotFound({ prettyMessage: 'project not found' })
+ if (branch) {
+ options.searchParams.branchName = `refs/heads/${branch}`
}
+
+ const { count, value } = await this.fetch({
+ url,
+ options,
+ schema: buildSchema,
+ httpErrors,
+ })
+
+ if (count !== 1) {
+ throw new NotFound({ prettyMessage: 'build pipeline not found' })
+ }
+
+ const result =
+ stage || job
+ ? await this.getStageOrJobResult(
+ organization,
+ projectId,
+ value[0].id,
+ stage,
+ job,
+ httpErrors,
+ )
+ : value[0].result
+ const status = this.constructor.resultMap[result] || result
return renderBuildStatusBadge({ status })
}
}
diff --git a/services/azure-devops/azure-devops-build.spec.js b/services/azure-devops/azure-devops-build.spec.js
new file mode 100644
index 0000000000000..386362bdcc569
--- /dev/null
+++ b/services/azure-devops/azure-devops-build.spec.js
@@ -0,0 +1,20 @@
+import { testAuth } from '../test-helpers.js'
+import AzureDevOpsBuild from './azure-devops-build.service.js'
+
+describe('AzureDevOpsBuild', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ // Exercise the overall-build path (no stage/job) so a single mocked
+ // request is sufficient; the auth header is sent the same way regardless.
+ return testAuth(
+ AzureDevOpsBuild,
+ 'BasicAuth',
+ {
+ count: 1,
+ value: [{ id: 12345, status: 'completed', result: 'succeeded' }],
+ },
+ { exampleOverride: { stage: undefined, job: undefined } },
+ )
+ })
+ })
+})
diff --git a/services/azure-devops/azure-devops-build.tester.js b/services/azure-devops/azure-devops-build.tester.js
index 6df777c07395b..8278c58daee52 100644
--- a/services/azure-devops/azure-devops-build.tester.js
+++ b/services/azure-devops/azure-devops-build.tester.js
@@ -1,24 +1,19 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// https://dev.azure.com/totodem/Shields.io is a public Azure DevOps project
// solely created for Shields.io testing.
-t.create('default branch')
- .get('/totodem/shields.io/2.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
+t.create('default branch').get('/totodem/shields.io/2.json').expectBadge({
+ label: 'build',
+ message: isBuildStatus,
+})
-t.create('named branch')
- .get('/totodem/shields.io/2/master.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
+t.create('named branch').get('/totodem/shields.io/2/master.json').expectBadge({
+ label: 'build',
+ message: isBuildStatus,
+})
t.create('stage badge')
.get('/totodem/Shields.io/5.json?stage=Successful%20Stage')
@@ -29,30 +24,100 @@ t.create('stage badge')
t.create('job badge')
.get(
- '/totodem/Shields.io/5.json?stage=Successful%20Stage&job=Successful%20Job'
+ '/totodem/Shields.io/5.json?stage=Successful%20Stage&job=Successful%20Job',
)
.expectBadge({
label: 'build',
message: isBuildStatus,
})
-t.create('never built definition')
+// A pipeline that exists but has no completed builds, and a bad org/project,
+// both surface as 'build pipeline not found' — consistent with the sibling
+// Azure DevOps badges (coverage, tests).
+t.create('unknown build definition')
.get('/swellaby/opensource/112.json')
- .expectBadge({ label: 'build', message: 'never built' })
+ .expectBadge({ label: 'build', message: 'build pipeline not found' })
-t.create('unknown definition')
- .get('/larsbrinkhoff/953a34b9-5966-4923-a48a-c41874cfb5f5/515.json')
- .expectBadge({ label: 'build', message: 'definition not found' })
-
-t.create('unknown project')
- .get('/larsbrinkhoff/foo/515.json')
- .expectBadge({ label: 'build', message: 'user or project not found' })
-
-t.create('unknown user')
+t.create('unknown user or project')
.get('/notarealuser/foo/515.json')
- .expectBadge({ label: 'build', message: 'user or project not found' })
+ .expectBadge({ label: 'build', message: 'build pipeline not found' })
// The following build definition has always a partially succeeded status
t.create('partially succeeded build')
.get('/totodem/shields.io/4/master.json')
.expectBadge({ label: 'build', message: 'passing', color: 'orange' })
+
+// Overall build is `succeeded` but the requested stage `failed` — the badge
+// must reflect the stage, proving the Timeline lookup is used.
+t.create('stage status reflects the stage, not the overall build')
+ .get('/totodem/someproject/5.json?stage=Failing%20Stage')
+ .intercept(nock =>
+ nock('https://dev.azure.com/totodem/someproject/_apis')
+ .get('/build/builds')
+ .query({
+ definitions: 5,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
+ })
+ .reply(200, {
+ count: 1,
+ value: [{ id: 999, status: 'completed', result: 'succeeded' }],
+ })
+ .get('/build/builds/999/timeline')
+ .reply(200, {
+ records: [
+ { type: 'Stage', name: 'Successful Stage', result: 'succeeded' },
+ { type: 'Stage', name: 'Failing Stage', result: 'failed' },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'build', message: 'failing', color: 'red' })
+
+// A job takes precedence over a stage, and timeline `succeededWithIssues`
+// maps to the orange partially-succeeded badge.
+t.create('job status (succeededWithIssues -> partially succeeded)')
+ .get('/totodem/someproject/5.json?stage=Successful%20Stage&job=Flaky%20Job')
+ .intercept(nock =>
+ nock('https://dev.azure.com/totodem/someproject/_apis')
+ .get('/build/builds')
+ .query({
+ definitions: 5,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
+ })
+ .reply(200, {
+ count: 1,
+ value: [{ id: 999, status: 'completed', result: 'succeeded' }],
+ })
+ .get('/build/builds/999/timeline')
+ .reply(200, {
+ records: [
+ { type: 'Job', name: 'Flaky Job', result: 'succeededWithIssues' },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'build', message: 'passing', color: 'orange' })
+
+t.create('unknown stage')
+ .get('/totodem/someproject/5.json?stage=Nonexistent%20Stage')
+ .intercept(nock =>
+ nock('https://dev.azure.com/totodem/someproject/_apis')
+ .get('/build/builds')
+ .query({
+ definitions: 5,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
+ })
+ .reply(200, {
+ count: 1,
+ value: [{ id: 999, status: 'completed', result: 'succeeded' }],
+ })
+ .get('/build/builds/999/timeline')
+ .reply(200, {
+ records: [{ type: 'Stage', name: 'Other Stage', result: 'succeeded' }],
+ }),
+ )
+ .expectBadge({ label: 'build', message: 'stage not found' })
diff --git a/services/azure-devops/azure-devops-coverage.service.js b/services/azure-devops/azure-devops-coverage.service.js
index 97cc0596bec6a..37f72e6e0811c 100644
--- a/services/azure-devops/azure-devops-coverage.service.js
+++ b/services/azure-devops/azure-devops-coverage.service.js
@@ -1,32 +1,27 @@
-'use strict'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js'
+import AzureDevOpsBase from './azure-devops-base.js'
-const Joi = require('@hapi/joi')
-const {
- coveragePercentage: coveragePercentageColor,
-} = require('../color-formatters')
-const AzureDevOpsBase = require('./azure-devops-base')
-const { keywords } = require('./azure-devops-helpers')
+const description = `
+[Azure Devops](https://dev.azure.com/) (formerly VSO, VSTS) is Microsoft Azure's CI/CD platform.
+
+To obtain your own badge, you need to get 3 pieces of information:
+\`ORGANIZATION\`, \`PROJECT_ID\` and \`DEFINITION_ID\`.
+
+First, you need to select your build definition and look at the url:
-const documentation = `
-
- To obtain your own badge, you need to get 3 pieces of information:
- ORGANIZATION, PROJECT and DEFINITION_ID.
-
-
- First, you need to select your build definition and look at the url:
-
-
- Your badge will then have the form:
- https://img.shields.io/azure-devops/coverage/ORGANIZATION/PROJECT/DEFINITION_ID.svg.
-
-
- Optionally, you can specify a named branch:
- https://img.shields.io/azure-devops/coverage/ORGANIZATION/PROJECT/DEFINITION_ID/NAMED_BRANCH.svg.
-
+
+Your badge will then have the form:
+\`https://img.shields.io/azure-devops/coverage/ORGANIZATION/PROJECT/DEFINITION_ID.svg\`.
+
+Optionally, you can specify a named branch:
+\`https://img.shields.io/azure-devops/coverage/ORGANIZATION/PROJECT/DEFINITION_ID/NAMED_BRANCH.svg\`.
`
+
const buildCodeCoverageSchema = Joi.object({
coverageData: Joi.array()
.items(
@@ -37,60 +32,71 @@ const buildCodeCoverageSchema = Joi.object({
label: Joi.string().required(),
total: Joi.number().required(),
covered: Joi.number().required(),
- })
+ }),
)
.min(1)
.required(),
- })
+ }),
)
.required(),
}).required()
-module.exports = class AzureDevOpsCoverage extends AzureDevOpsBase {
- static get category() {
- return 'coverage'
- }
+export default class AzureDevOpsCoverage extends AzureDevOpsBase {
+ static category = 'coverage'
- static get route() {
- return {
- base: 'azure-devops/coverage',
- pattern: ':organization/:project/:definitionId/:branch*',
- }
+ static route = {
+ base: 'azure-devops/coverage',
+ pattern: ':organization/:project/:definitionId/:branch*',
}
- static get examples() {
- return [
- {
- title: 'Azure DevOps coverage',
- pattern: ':organization/:project/:definitionId',
- namedParams: {
- organization: 'swellaby',
- project: 'opensource',
- definitionId: '25',
- },
- staticPreview: this.render({ coverage: 100 }),
- keywords,
- documentation,
+ static openApi = {
+ '/azure-devops/coverage/{organization}/{project}/{definitionId}': {
+ get: {
+ summary: 'Azure DevOps coverage',
+ description,
+ parameters: pathParams(
+ {
+ name: 'organization',
+ example: 'swellaby',
+ },
+ {
+ name: 'project',
+ example: 'opensource',
+ },
+ {
+ name: 'definitionId',
+ example: '25',
+ },
+ ),
},
- {
- title: 'Azure DevOps coverage (branch)',
- pattern: ':organization/:project/:definitionId/:branch',
- namedParams: {
- organization: 'swellaby',
- project: 'opensource',
- definitionId: '25',
- branch: 'master',
- },
- staticPreview: this.render({ coverage: 100 }),
- keywords,
- documentation,
+ },
+ '/azure-devops/coverage/{organization}/{project}/{definitionId}/{branch}': {
+ get: {
+ summary: 'Azure DevOps coverage (branch)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'organization',
+ example: 'swellaby',
+ },
+ {
+ name: 'project',
+ example: 'opensource',
+ },
+ {
+ name: 'definitionId',
+ example: '25',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'coverage' }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ coverage }) {
return {
@@ -100,7 +106,7 @@ module.exports = class AzureDevOpsCoverage extends AzureDevOpsBase {
}
async handle({ organization, project, definitionId, branch }) {
- const errorMessages = {
+ const httpErrors = {
404: 'build pipeline or coverage not found',
}
const buildId = await this.getLatestCompletedBuildId(
@@ -108,12 +114,12 @@ module.exports = class AzureDevOpsCoverage extends AzureDevOpsBase {
project,
definitionId,
branch,
- errorMessages
+ httpErrors,
)
// Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/test/code%20coverage/get%20build%20code%20coverage?view=azure-devops-rest-5.0
const url = `https://dev.azure.com/${organization}/${project}/_apis/test/codecoverage`
const options = {
- qs: {
+ searchParams: {
buildId,
'api-version': '5.0-preview.1',
},
@@ -122,7 +128,7 @@ module.exports = class AzureDevOpsCoverage extends AzureDevOpsBase {
url,
options,
schema: buildCodeCoverageSchema,
- errorMessages,
+ httpErrors,
})
let covered = 0
diff --git a/services/azure-devops/azure-devops-coverage.spec.js b/services/azure-devops/azure-devops-coverage.spec.js
new file mode 100644
index 0000000000000..9c05337bd0db0
--- /dev/null
+++ b/services/azure-devops/azure-devops-coverage.spec.js
@@ -0,0 +1,33 @@
+import { testAuth } from '../test-helpers.js'
+import AzureDevOpsCoverage from './azure-devops-coverage.service.js'
+
+describe('AzureDevOpsCoverage', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ AzureDevOpsCoverage,
+ 'BasicAuth',
+ {
+ coverageData: [
+ {
+ coverageStats: [
+ {
+ label: 'Coverage',
+ total: 95,
+ covered: 93,
+ },
+ ],
+ },
+ ],
+ count: 1,
+ value: [
+ {
+ id: 90395,
+ },
+ ],
+ },
+ { multipleRequests: true },
+ )
+ })
+ })
+})
diff --git a/services/azure-devops/azure-devops-coverage.tester.js b/services/azure-devops/azure-devops-coverage.tester.js
index 859ac35c4d7a9..7f0e8d2429500 100644
--- a/services/azure-devops/azure-devops-coverage.tester.js
+++ b/services/azure-devops/azure-devops-coverage.tester.js
@@ -1,11 +1,10 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const org = 'swellaby'
const project = 'opensource'
-const linuxDefinitionId = 21
+const linuxDefinitionId = 25
const macDefinitionId = 26
const windowsDefinitionId = 24
const nonExistentDefinitionId = 234421
@@ -74,9 +73,7 @@ t.create('unknown build definition')
t.create('404 latest build error response')
.get(mockBadgeUriPath)
.intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(404)
+ nock(azureDevOpsApiBaseUri).get(mockLatestBuildApiUriPath).reply(404),
)
.expectBadge({
label: 'coverage',
@@ -88,12 +85,12 @@ t.create('no build response')
.intercept(nock =>
nock(azureDevOpsApiBaseUri)
.get(
- `/build/builds?definitions=${nonExistentDefinitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`
+ `/build/builds?definitions=${nonExistentDefinitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`,
)
.reply(200, {
count: 0,
value: [],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: 'build pipeline not found' })
@@ -104,7 +101,7 @@ t.create('404 code coverage error response')
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockCodeCoverageApiUriPath)
- .reply(404)
+ .reply(404),
)
.expectBadge({
label: 'coverage',
@@ -118,7 +115,7 @@ t.create('invalid code coverage response')
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockCodeCoverageApiUriPath)
- .reply(200, {})
+ .reply(200, {}),
)
.expectBadge({ label: 'coverage', message: 'invalid response data' })
@@ -129,7 +126,7 @@ t.create('no code coverage reports')
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockCodeCoverageApiUriPath)
- .reply(200, { coverageData: [] })
+ .reply(200, { coverageData: [] }),
)
.expectBadge({ label: 'coverage', message: '0%' })
@@ -140,7 +137,7 @@ t.create('no code coverage reports')
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockCodeCoverageApiUriPath)
- .reply(200, { coverageData: [] })
+ .reply(200, { coverageData: [] }),
)
.expectBadge({ label: 'coverage', message: '0%' })
@@ -157,7 +154,7 @@ t.create('no line coverage stats')
coverageStats: [branchCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: '0%' })
@@ -174,7 +171,7 @@ t.create('single line coverage stats')
coverageStats: [firstLinesCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: expCoverageSingleReport })
@@ -191,7 +188,7 @@ t.create('mixed line and branch coverage stats')
coverageStats: [firstLinesCovStat, branchCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: expCoverageSingleReport })
@@ -212,7 +209,7 @@ t.create('multiple line coverage stat reports')
],
},
],
- })
+ }),
)
t.create('single JaCoCo style line coverage stats')
@@ -228,7 +225,7 @@ t.create('single JaCoCo style line coverage stats')
coverageStats: [firstLineCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: expCoverageSingleReport })
@@ -245,7 +242,7 @@ t.create('mixed JaCoCo style line and branch coverage stats')
coverageStats: [firstLineCovStat, branchCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: expCoverageSingleReport })
@@ -262,6 +259,6 @@ t.create('multiple JaCoCo style line coverage stat reports')
coverageStats: [firstLineCovStat, branchCovStat, secondLineCovStat],
},
],
- })
+ }),
)
.expectBadge({ label: 'coverage', message: expCoverageMultipleReports })
diff --git a/services/azure-devops/azure-devops-helpers.js b/services/azure-devops/azure-devops-helpers.js
index 82cb62d8ab6a9..269d283e891d6 100644
--- a/services/azure-devops/azure-devops-helpers.js
+++ b/services/azure-devops/azure-devops-helpers.js
@@ -1,9 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-
-const keywords = ['vso', 'vsts', 'azure-devops']
+import Joi from 'joi'
+import { isBuildStatus } from '../build-status.js'
const schema = Joi.object({
message: Joi.alternatives()
@@ -12,20 +8,20 @@ const schema = Joi.object({
Joi.equal('unknown'),
Joi.equal('set up now'),
Joi.equal('never built'),
- Joi.equal('never deployed')
+ Joi.equal('never deployed'),
)
.required(),
}).required()
-async function fetch(serviceInstance, { url, qs = {}, errorMessages }) {
+async function fetch(serviceInstance, { url, searchParams = {}, httpErrors }) {
// Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/vsts/build/status/get
const { message: status } = await serviceInstance._requestSvg({
schema,
url,
- options: { qs },
- errorMessages,
+ options: { searchParams },
+ httpErrors,
})
return { status }
}
-module.exports = { keywords, fetch }
+export { fetch }
diff --git a/services/azure-devops/azure-devops-release.service.js b/services/azure-devops/azure-devops-release.service.js
index 0bbc6ab41f694..1af4ea8246dae 100644
--- a/services/azure-devops/azure-devops-release.service.js
+++ b/services/azure-devops/azure-devops-release.service.js
@@ -1,67 +1,65 @@
-'use strict'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
+import { fetch } from './azure-devops-helpers.js'
-const { renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService } = require('..')
-const { keywords, fetch } = require('./azure-devops-helpers')
+const description = `
+To obtain your own badge, you need to get 4 pieces of information:
+\`ORGANIZATION\`, \`PROJECT_ID\`, \`DEFINITION_ID\` and \`ENVIRONMENT_ID\`.
+
+First, you need to enable badges for each required environments in the options of your release definition.
+Once you have save the change, look at badge url:
-const documentation = `
-
- To obtain your own badge, you need to get 4 pieces of information:
- ORGANIZATION, PROJECT_ID, DEFINITION_ID and ENVIRONMENT_ID.
-
-
- First, you need to enable badges for each required environments in the options of your release definition.
- Once you have save the change, look at badge url:
-
-
- Your badge will then have the form:
- https://img.shields.io/vso/release/ORGANIZATION/PROJECT_ID/DEFINITION_ID/ENVIRONMENT_ID.svg.
-
+
+Your badge will then have the form:
+\`https://img.shields.io/vso/release/ORGANIZATION/PROJECT_ID/DEFINITION_ID/ENVIRONMENT_ID.svg\`.
`
-module.exports = class AzureDevOpsRelease extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
+export default class AzureDevOpsRelease extends BaseSvgScrapingService {
+ static category = 'build'
- static get route() {
- return {
- base: 'azure-devops/release',
- pattern: ':organization/:projectId/:definitionId/:environmentId',
- }
+ static route = {
+ base: 'azure-devops/release',
+ pattern: ':organization/:projectId/:definitionId/:environmentId',
}
- static get examples() {
- return [
+ static openApi = {
+ '/azure-devops/release/{organization}/{projectId}/{definitionId}/{environmentId}':
{
- title: 'Azure DevOps releases',
- namedParams: {
- organization: 'totodem',
- projectId: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
- definitionId: '1',
- environmentId: '1',
+ get: {
+ summary: 'Azure DevOps releases',
+ description,
+ parameters: pathParams(
+ {
+ name: 'organization',
+ example: 'totodem',
+ },
+ {
+ name: 'projectId',
+ example: '8cf3ec0e-d0c2-4fcd-8206-ad204f254a96',
+ },
+ {
+ name: 'definitionId',
+ example: '1',
+ },
+ {
+ name: 'environmentId',
+ example: '1',
+ },
+ ),
},
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
},
- ]
}
- static get defaultBadgeData() {
- return {
- label: 'deployment',
- }
- }
+ static defaultBadgeData = { label: 'deployment' }
async handle({ organization, projectId, definitionId, environmentId }) {
// Microsoft documentation: ?
const props = await fetch(this, {
url: `https://vsrm.dev.azure.com/${organization}/_apis/public/Release/badge/${projectId}/${definitionId}/${environmentId}`,
- errorMessages: {
+ httpErrors: {
400: 'project not found',
404: 'user or environment not found',
500: 'inaccessible or definition not found',
diff --git a/services/azure-devops/azure-devops-release.tester.js b/services/azure-devops/azure-devops-release.tester.js
index 4a6cb5cda6239..7ffa1d94e3275 100644
--- a/services/azure-devops/azure-devops-release.tester.js
+++ b/services/azure-devops/azure-devops-release.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// https://dev.azure.com/totodem/Shields.io is a public Azure DevOps project
// solely created for Shields.io testing.
@@ -38,9 +37,7 @@ t.create('unknown project')
.get('/totodem/515/515/515.json')
.expectBadge({ label: 'deployment', message: 'project not found' })
-t.create('unknown user')
- .get('/this-repo/does-not-exist/1/2.json')
- .expectBadge({
- label: 'deployment',
- message: 'user or environment not found',
- })
+t.create('unknown user').get('/this-repo/does-not-exist/1/2.json').expectBadge({
+ label: 'deployment',
+ message: 'user or environment not found',
+})
diff --git a/services/azure-devops/azure-devops-tests.service.js b/services/azure-devops/azure-devops-tests.service.js
index 6a4471b9b1d5c..065e497b3ee7c 100644
--- a/services/azure-devops/azure-devops-tests.service.js
+++ b/services/azure-devops/azure-devops-tests.service.js
@@ -1,48 +1,33 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
+import Joi from 'joi'
+import { pathParam } from '../index.js'
+import {
+ testResultOpenApiQueryParams,
testResultQueryParamSchema,
renderTestResultBadge,
-} = require('../test-results')
-const AzureDevOpsBase = require('./azure-devops-base')
-
-const commonAttrs = {
- keywords: ['vso', 'vsts', 'azure-devops'],
- documentation: `
-
- To obtain your own badge, you need to get 3 pieces of information:
- ORGANIZATION, PROJECT and DEFINITION_ID.
-
-
- First, you need to select your build definition and look at the url:
-
+ documentation as commonDocumentation,
+} from '../test-results.js'
+import AzureDevOpsBase from './azure-devops-base.js'
+
+const description = `
+[Azure Devops](https://dev.azure.com/) (formerly VSO, VSTS) is Microsoft Azure's CI/CD platform.
+
+To obtain your own badge, you need to get 3 pieces of information:
+\`ORGANIZATION\`, \`PROJECT\`, \`DEFINITION_ID\`.
+
+First, you need to select your build definition and look at the url:
+
-
- Your badge will then have the form:
- https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg.
-
-
- Optionally, you can specify a named branch:
- https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID/NAMED_BRANCH.svg.
-
-
- You may change the "passed", "failed" and "skipped" text on this badge by supplying query parameters &passed_label=, &failed_label= and &skipped_label= respectively.
-
- There is also a &compact_message query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
-
- For example, if you want to use a different terminology:
-
- /azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg?passed_label=good&failed_label=bad&skipped_label=n%2Fa
-
- Or, use symbols:
-
- /azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg?compact_message&passed_label=%F0%9F%8E%89&failed_label=%F0%9F%92%A2&skipped_label=%F0%9F%A4%B7
-
-`,
-}
+
+Your badge will then have the form:
+\`https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg\`.
+
+Optionally, you can specify a named branch:
+\`https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID/NAMED_BRANCH.svg\`.
+
+${commonDocumentation}
+`
const buildTestResultSummarySchema = Joi.object({
aggregatedResultsAnalysis: Joi.object({
@@ -61,104 +46,64 @@ const buildTestResultSummarySchema = Joi.object({
}).required(),
}).required()
-module.exports = class AzureDevOpsTests extends AzureDevOpsBase {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'azure-devops/tests',
- pattern: ':organization/:project/:definitionId/:branch*',
- queryParamSchema: testResultQueryParamSchema,
- }
+export default class AzureDevOpsTests extends AzureDevOpsBase {
+ static category = 'test-results'
+ static route = {
+ base: 'azure-devops/tests',
+ pattern: ':organization/:project/:definitionId/:branch*',
+ queryParamSchema: testResultQueryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Azure DevOps tests',
- pattern: ':organization/:project/:definitionId',
- namedParams: {
- organization: 'azuredevops-powershell',
- project: 'azuredevops-powershell',
- definitionId: '1',
- },
- staticPreview: this.render({
- passed: 20,
- failed: 1,
- skipped: 1,
- total: 22,
- }),
- ...commonAttrs,
- },
- {
- title: 'Azure DevOps tests (branch)',
- pattern: ':organization/:project/:definitionId/:branch',
- namedParams: {
- organization: 'azuredevops-powershell',
- project: 'azuredevops-powershell',
- definitionId: '1',
- branch: 'master',
- },
- staticPreview: this.render({
- passed: 20,
- failed: 1,
- skipped: 1,
- total: 22,
- }),
- ...commonAttrs,
+ static openApi = {
+ '/azure-devops/tests/{organization}/{project}/{definitionId}': {
+ get: {
+ summary: 'Azure DevOps tests',
+ description,
+ parameters: [
+ pathParam({
+ name: 'organization',
+ example: 'azuredevops-powershell',
+ }),
+ pathParam({
+ name: 'project',
+ example: 'azuredevops-powershell',
+ }),
+ pathParam({
+ name: 'definitionId',
+ example: '1',
+ }),
+ ...testResultOpenApiQueryParams,
+ ],
},
- {
- title: 'Azure DevOps tests (compact)',
- pattern: ':organization/:project/:definitionId',
- namedParams: {
- organization: 'azuredevops-powershell',
- project: 'azuredevops-powershell',
- definitionId: '1',
- },
- queryParams: {
- compact_message: null,
- },
- staticPreview: this.render({
- passed: 20,
- failed: 1,
- skipped: 1,
- total: 22,
- isCompact: true,
- }),
- ...commonAttrs,
+ },
+ '/azure-devops/tests/{organization}/{project}/{definitionId}/{branch}': {
+ get: {
+ summary: 'Azure DevOps tests (branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'organization',
+ example: 'azuredevops-powershell',
+ }),
+ pathParam({
+ name: 'project',
+ example: 'azuredevops-powershell',
+ }),
+ pathParam({
+ name: 'definitionId',
+ example: '1',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ ...testResultOpenApiQueryParams,
+ ],
},
- {
- title: 'Azure DevOps tests with custom labels',
- pattern: ':organization/:project/:definitionId',
- namedParams: {
- organization: 'azuredevops-powershell',
- project: 'azuredevops-powershell',
- definitionId: '1',
- },
- queryParams: {
- passed_label: 'good',
- failed_label: 'bad',
- skipped_label: 'n/a',
- },
- staticPreview: this.render({
- passed: 20,
- failed: 1,
- skipped: 1,
- total: 22,
- passedLabel: 'good',
- failedLabel: 'bad',
- skippedLabel: 'n/a',
- }),
- ...commonAttrs,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'tests' }
- }
+ static defaultBadgeData = { label: 'tests' }
static render({
passed,
@@ -182,16 +127,17 @@ module.exports = class AzureDevOpsTests extends AzureDevOpsBase {
})
}
- async handle(
- { organization, project, definitionId, branch },
- {
- compact_message: compactMessage,
- passed_label: passedLabel,
- failed_label: failedLabel,
- skipped_label: skippedLabel,
- }
- ) {
- const errorMessages = {
+ static transform({ aggregatedResultsAnalysis }) {
+ const { totalTests: total, resultsByOutcome } = aggregatedResultsAnalysis
+ const passed = resultsByOutcome.Passed ? resultsByOutcome.Passed.count : 0
+ const failed = resultsByOutcome.Failed ? resultsByOutcome.Failed.count : 0
+ // assume the rest has been skipped
+ const skipped = total - passed - failed
+ return { passed, failed, skipped, total }
+ }
+
+ async fetchTestResults({ organization, project, definitionId, branch }) {
+ const httpErrors = {
404: 'build pipeline or test result summary not found',
}
const buildId = await this.getLatestCompletedBuildId(
@@ -199,37 +145,36 @@ module.exports = class AzureDevOpsTests extends AzureDevOpsBase {
project,
definitionId,
branch,
- errorMessages
+ httpErrors,
)
// https://dev.azure.com/azuredevops-powershell/azuredevops-powershell/_apis/test/ResultSummaryByBuild?buildId=20
-
- const json = await this.fetch({
+ return await this.fetch({
url: `https://dev.azure.com/${organization}/${project}/_apis/test/ResultSummaryByBuild`,
options: {
- qs: { buildId },
+ searchParams: { buildId },
},
schema: buildTestResultSummarySchema,
- errorMessages,
+ httpErrors,
})
+ }
- const total = json.aggregatedResultsAnalysis.totalTests
-
- let passed = 0
- const passedTests = json.aggregatedResultsAnalysis.resultsByOutcome.Passed
- if (passedTests) {
- passed = passedTests.count
- }
-
- let failed = 0
- const failedTests = json.aggregatedResultsAnalysis.resultsByOutcome.Failed
- if (failedTests) {
- failed = failedTests.count
- }
-
- // assume the rest has been skipped
- const skipped = total - passed - failed
- const isCompact = compactMessage !== undefined
+ async handle(
+ { organization, project, definitionId, branch },
+ {
+ compact_message: compactMessage,
+ passed_label: passedLabel,
+ failed_label: failedLabel,
+ skipped_label: skippedLabel,
+ },
+ ) {
+ const json = await this.fetchTestResults({
+ organization,
+ project,
+ definitionId,
+ branch,
+ })
+ const { passed, failed, skipped, total } = this.constructor.transform(json)
return this.constructor.render({
passed,
failed,
@@ -238,7 +183,7 @@ module.exports = class AzureDevOpsTests extends AzureDevOpsBase {
passedLabel,
failedLabel,
skippedLabel,
- isCompact,
+ isCompact: compactMessage !== undefined,
})
}
}
diff --git a/services/azure-devops/azure-devops-tests.spec.js b/services/azure-devops/azure-devops-tests.spec.js
new file mode 100644
index 0000000000000..c139576779584
--- /dev/null
+++ b/services/azure-devops/azure-devops-tests.spec.js
@@ -0,0 +1,32 @@
+import { testAuth } from '../test-helpers.js'
+import AzureDevOpsTests from './azure-devops-tests.service.js'
+
+describe('AzureDevOpsTests', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ AzureDevOpsTests,
+ 'BasicAuth',
+ {
+ aggregatedResultsAnalysis: {
+ totalTests: 95,
+ resultsByOutcome: {
+ Passed: {
+ count: 93,
+ },
+ },
+ },
+ count: 1,
+ value: [
+ {
+ id: 90395,
+ },
+ ],
+ },
+ {
+ multipleRequests: true,
+ },
+ )
+ })
+ })
+})
diff --git a/services/azure-devops/azure-devops-tests.tester.js b/services/azure-devops/azure-devops-tests.tester.js
index 5db7e4f236777..59e315de157c5 100644
--- a/services/azure-devops/azure-devops-tests.tester.js
+++ b/services/azure-devops/azure-devops-tests.tester.js
@@ -1,268 +1,101 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const org = 'azuredevops-powershell'
-const project = 'azuredevops-powershell'
-const definitionId = 1
-const nonExistentDefinitionId = 9999
-const buildId = 20
-const uriPrefix = `/${org}/${project}`
-const azureDevOpsApiBaseUri = `https://dev.azure.com/${org}/${project}/_apis`
-const mockBadgeUriPath = `${uriPrefix}/${definitionId}`
-const mockBadgeUri = `${mockBadgeUriPath}.json`
-const mockBranchBadgeUri = `${mockBadgeUriPath}/master.json`
-const mockLatestBuildApiUriPath = `/build/builds?definitions=${definitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`
-const mockLatestBranchBuildApiUriPath = `/build/builds?definitions=${definitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4&branchName=refs%2Fheads%2Fmaster`
-const mockNonExistentBuildApiUriPath = `/build/builds?definitions=${nonExistentDefinitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`
-const mockTestResultSummaryApiUriPath = `/test/ResultSummaryByBuild?buildId=${buildId}`
-const latestBuildResponse = {
- count: 1,
- value: [{ id: buildId }],
-}
-const mockEmptyTestResultSummaryResponse = {
- aggregatedResultsAnalysis: {
- totalTests: 0,
- resultsByOutcome: {},
- },
-}
-const mockTestResultSummaryResponse = {
- aggregatedResultsAnalysis: {
- totalTests: 3,
- resultsByOutcome: {
- Passed: {
- count: 1,
- },
- Failed: {
- count: 1,
- },
- Skipped: {
- count: 1,
- },
- },
- },
-}
-const mockTestResultSummarySetup = nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(200, latestBuildResponse)
- .get(mockTestResultSummaryApiUriPath)
- .reply(200, mockTestResultSummaryResponse)
-const mockBranchTestResultSummarySetup = nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBranchBuildApiUriPath)
- .reply(200, latestBuildResponse)
- .get(mockTestResultSummaryApiUriPath)
- .reply(200, mockTestResultSummaryResponse)
-
-const expectedDefaultAzureDevOpsTestTotals = '1 passed, 1 failed, 1 skipped'
-const expectedCompactAzureDevOpsTestTotals = '✔ 1 | ✘ 1 | ➟ 1'
-const expectedCustomAzureDevOpsTestTotals = '1 good, 1 bad, 1 n/a'
-const expectedCompactCustomAzureDevOpsTestTotals = '💃 1 | 🤦♀️ 1 | 🤷 1'
-
-function getLabelRegex(label, isCompact) {
- return isCompact ? `(?:${label} [0-9]*)` : `(?:[0-9]* ${label})`
-}
-
-function isAzureDevOpsTestTotals(
- passedLabel,
- failedLabel,
- skippedLabel,
- isCompact
-) {
- const passedRegex = getLabelRegex(passedLabel, isCompact)
- const failedRegex = getLabelRegex(failedLabel, isCompact)
- const skippedRegex = getLabelRegex(skippedLabel, isCompact)
- const separator = isCompact ? ' | ' : ', '
- const regexStrings = [
- `^${passedRegex}$`,
- `^${failedRegex}$`,
- `^${skippedRegex}$`,
- `^${passedRegex}${separator}${failedRegex}$`,
- `^${failedRegex}${separator}${skippedRegex}$`,
- `^${passedRegex}${separator}${skippedRegex}$`,
- `^${passedRegex}${separator}${failedRegex}${separator}${skippedRegex}$`,
- `^no tests$`,
- ]
-
- return Joi.alternatives().try(
- ...regexStrings.map(regexStr => Joi.string().regex(new RegExp(regexStr)))
- )
-}
-
-const isDefaultAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
- 'passed',
- 'failed',
- 'skipped'
-)
-const isCompactAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
- '✔',
- '✘',
- '➟',
- true
-)
-const isCustomAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
- 'good',
- 'bad',
- 'n\\/a'
-)
-const isCompactCustomAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
- '💃',
- '🤦♀️',
- '🤷',
- true
-)
+import {
+ isDefaultTestTotals,
+ isDefaultCompactTestTotals,
+ isCustomTestTotals,
+ isCustomCompactTestTotals,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('unknown build definition')
- .get(`${uriPrefix}/${nonExistentDefinitionId}.json`)
+ .get('/swellaby/opensource/99999999.json')
.expectBadge({ label: 'tests', message: 'build pipeline not found' })
t.create('404 latest build error response')
- .get(mockBadgeUri)
+ .get('/swellaby/fake/14.json')
.intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(404)
+ nock('https://dev.azure.com/swellaby/fake/_apis')
+ .get('/build/builds')
+ .query({
+ definitions: 14,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
+ })
+ .reply(404),
)
.expectBadge({
label: 'tests',
message: 'build pipeline or test result summary not found',
})
-t.create('no build response')
- .get(`${uriPrefix}/${nonExistentDefinitionId}.json`)
- .intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockNonExistentBuildApiUriPath)
- .reply(200, {
- count: 0,
- value: [],
- })
- )
- .expectBadge({ label: 'tests', message: 'build pipeline not found' })
-
t.create('no test result summary response')
- .get(mockBadgeUri)
+ .get('/swellaby/fake/14.json')
.intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(200, latestBuildResponse)
- .get(mockTestResultSummaryApiUriPath)
- .reply(404)
+ nock('https://dev.azure.com/swellaby/fake/_apis')
+ .get('/build/builds')
+ .query({
+ definitions: 14,
+ $top: 1,
+ statusFilter: 'completed',
+ 'api-version': '5.0-preview.4',
+ })
+ .reply(200, { count: 1, value: [{ id: 1234 }] })
+ .get('/test/ResultSummaryByBuild')
+ .query({ buildId: 1234 })
+ .reply(404),
)
.expectBadge({
label: 'tests',
message: 'build pipeline or test result summary not found',
})
-t.create('invalid test result summary response')
- .get(mockBadgeUri)
- .intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(200, latestBuildResponse)
- .get(mockTestResultSummaryApiUriPath)
- .reply(200, {})
- )
- .expectBadge({ label: 'tests', message: 'invalid response data' })
+t.create('no build response')
+ .get('/swellaby/opensource/174.json')
+ .expectBadge({ label: 'tests', message: 'build pipeline not found' })
t.create('no tests in test result summary response')
- .get(mockBadgeUri)
- .intercept(nock =>
- nock(azureDevOpsApiBaseUri)
- .get(mockLatestBuildApiUriPath)
- .reply(200, latestBuildResponse)
- .get(mockTestResultSummaryApiUriPath)
- .reply(200, mockEmptyTestResultSummaryResponse)
- )
+ .get('/swellaby/opensource/14.json')
.expectBadge({ label: 'tests', message: 'no tests' })
-t.create('test status')
- .get(mockBadgeUri)
- .intercept(mockTestResultSummarySetup)
- .expectBadge({
- label: 'tests',
- message: expectedDefaultAzureDevOpsTestTotals,
- })
+t.create('test status').get('/swellaby/opensource/25.json').expectBadge({
+ label: 'tests',
+ message: isDefaultTestTotals,
+})
t.create('test status on branch')
- .get(mockBranchBadgeUri)
- .intercept(mockBranchTestResultSummarySetup)
+ .get('/swellaby/opensource/25/master.json')
.expectBadge({
label: 'tests',
- message: expectedDefaultAzureDevOpsTestTotals,
+ message: isDefaultTestTotals,
})
t.create('test status with compact message')
- .get(mockBadgeUri, {
+ .get('/swellaby/opensource/25.json', {
qs: {
compact_message: null,
},
})
- .intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
- message: expectedCompactAzureDevOpsTestTotals,
+ message: isDefaultCompactTestTotals,
})
t.create('test status with custom labels')
- .get(mockBadgeUri, {
+ .get('/swellaby/opensource/25.json', {
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
})
- .intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
- message: expectedCustomAzureDevOpsTestTotals,
+ message: isCustomTestTotals,
})
t.create('test status with compact message and custom labels')
- .get(mockBadgeUri, {
- qs: {
- compact_message: null,
- passed_label: '💃',
- failed_label: '🤦♀️',
- skipped_label: '🤷',
- },
- })
- .intercept(mockTestResultSummarySetup)
- .expectBadge({
- label: 'tests',
- message: expectedCompactCustomAzureDevOpsTestTotals,
- })
-
-t.create('live test status')
- .get(mockBadgeUri)
- .expectBadge({ label: 'tests', message: isDefaultAzureDevOpsTestTotals })
-
-t.create('live test status on branch')
- .get(mockBranchBadgeUri)
- .expectBadge({ label: 'tests', message: isDefaultAzureDevOpsTestTotals })
-
-t.create('live test status with compact message')
- .get(mockBadgeUri, {
- qs: {
- compact_message: null,
- },
- })
- .expectBadge({ label: 'tests', message: isCompactAzureDevOpsTestTotals })
-
-t.create('live test status with custom labels')
- .get(mockBadgeUri, {
- qs: {
- passed_label: 'good',
- failed_label: 'bad',
- skipped_label: 'n/a',
- },
- })
- .expectBadge({ label: 'tests', message: isCustomAzureDevOpsTestTotals })
-
-t.create('live test status with compact message and custom labels')
- .get(mockBadgeUri, {
+ .get('/swellaby/opensource/25.json', {
qs: {
compact_message: null,
passed_label: '💃',
@@ -272,5 +105,5 @@ t.create('live test status with compact message and custom labels')
})
.expectBadge({
label: 'tests',
- message: isCompactCustomAzureDevOpsTestTotals,
+ message: isCustomCompactTestTotals,
})
diff --git a/services/azure-devops/vso-redirect.service.js b/services/azure-devops/vso-redirect.service.js
index a8b5d8435f7f9..642a5c53fa9e6 100644
--- a/services/azure-devops/vso-redirect.service.js
+++ b/services/azure-devops/vso-redirect.service.js
@@ -1,31 +1,22 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
- redirector({
- category: 'build',
- route: {
- base: 'vso/build',
- pattern: ':organization/:projectId/:definitionId/:branch*',
- },
- transformPath: ({ organization, projectId, definitionId, branch }) => {
- let path = `/azure-devops/build/${organization}/${projectId}/${definitionId}`
- if (branch) {
- path += `/${branch}`
- }
- return path
- },
- dateAdded: new Date('2019-02-08'),
- }),
- redirector({
- category: 'build',
- route: {
- base: 'vso/release',
- pattern: ':organization/:projectId/:definitionId/:environmentId',
- },
- transformPath: ({ organization, projectId, definitionId, environmentId }) =>
- `/azure-devops/release/${organization}/${projectId}/${definitionId}/${environmentId}`,
- dateAdded: new Date('2019-02-08'),
- }),
-]
+export const VsoBuildRedirector = retiredService({
+ category: 'build',
+ label: 'vso',
+ route: {
+ base: 'vso/build',
+ pattern: ':organization/:projectId/:definitionId/:branch*',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
+export const VsoReleaseRedirector = retiredService({
+ category: 'build',
+ label: 'vso',
+ route: {
+ base: 'vso/release',
+ pattern: ':organization/:projectId/:definitionId/:environmentId',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/azure-devops/vso-redirect.tester.js b/services/azure-devops/vso-redirect.tester.js
index 7cd544b7d9421..89b5071ee2c29 100644
--- a/services/azure-devops/vso-redirect.tester.js
+++ b/services/azure-devops/vso-redirect.tester.js
@@ -1,26 +1,27 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'vso',
title: 'VSO',
-}))
+})
t.create('Build: default branch')
- .get('/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2.svg')
- .expectRedirect(
- '/azure-devops/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2.svg'
- )
+ .get('/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2.json')
+ .expectBadge({
+ label: 'vso',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('Build: named branch')
- .get('/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2/master.svg')
- .expectRedirect(
- '/azure-devops/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2/master.svg'
- )
+ .get('/build/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/2/master.json')
+ .expectBadge({
+ label: 'vso',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('Release status')
- .get('/release/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/1/1.svg')
- .expectRedirect(
- '/azure-devops/release/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/1/1.svg'
- )
+ .get('/release/totodem/8cf3ec0e-d0c2-4fcd-8206-ad204f254a96/1/1.json')
+ .expectBadge({
+ label: 'vso',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/beerpay/beerpay.service.js b/services/beerpay/beerpay.service.js
deleted file mode 100644
index 9e9619a09f4e3..0000000000000
--- a/services/beerpay/beerpay.service.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- total_amount: Joi.number()
- .min(0)
- .required(),
-}).required()
-
-module.exports = class Beerpay extends BaseJsonService {
- static get category() {
- return 'funding'
- }
-
- static get route() {
- return {
- base: 'beerpay',
- pattern: ':user/:project',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Beerpay',
- namedParams: { user: 'hashdog', project: 'scrapfy-chrome-extension' },
- staticPreview: this.render({ totalAmount: 10 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'beerpay',
- }
- }
-
- static render({ totalAmount }) {
- return {
- message: `$${totalAmount}`,
- color: totalAmount > 0 ? 'blue' : 'red',
- }
- }
-
- async handle({ user, project }) {
- // e.g. JSON response: https://beerpay.io/api/v1/beerpay/projects/beerpay.io
- // e.g. SVG badge: https://beerpay.io/beerpay/beerpay.io/badge.svg?style=flat-square
- const { total_amount: totalAmount } = await this._requestJson({
- schema,
- url: `https://beerpay.io/api/v1/${user}/projects/${project}`,
- errorMessages: {
- 404: 'project not found',
- },
- })
- return this.constructor.render({ totalAmount })
- }
-}
diff --git a/services/beerpay/beerpay.tester.js b/services/beerpay/beerpay.tester.js
deleted file mode 100644
index c9cf089de3d5c..0000000000000
--- a/services/beerpay/beerpay.tester.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const amountOfMoney = withRegex(/^\$[0-9]+(\.[0-9]+)?/)
-
-t.create('funding')
- .get('/hashdog/scrapfy-chrome-extension.json')
- .expectBadge({
- label: 'beerpay',
- message: amountOfMoney,
- })
-
-t.create('funding (unknown project)')
- .get('/hashdog/not-a-real-project.json')
- .expectBadge({
- label: 'beerpay',
- message: 'project not found',
- })
diff --git a/services/bintray/bintray.service.js b/services/bintray/bintray.service.js
deleted file mode 100644
index fc5c97b5cbd06..0000000000000
--- a/services/bintray/bintray.service.js
+++ /dev/null
@@ -1,65 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object()
- .keys({
- name: Joi.string().required(),
- })
- .required()
-
-module.exports = class Bintray extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'bintray/v',
- pattern: ':subject/:repo/:packageName',
- }
- }
-
- static get auth() {
- return {
- userKey: 'bintray_user',
- passKey: 'bintray_apikey',
- authorizedOrigins: ['https://bintray.com'],
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Bintray',
- staticPreview: renderVersionBadge({ version: '1.6.0' }),
- namedParams: {
- subject: 'asciidoctor',
- repo: 'maven',
- packageName: 'asciidoctorj',
- },
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'bintray' }
- }
-
- async fetch({ subject, repo, packageName }) {
- // https://bintray.com/docs/api/#_get_version
- return this._requestJson(
- this.authHelper.withBasicAuth({
- schema,
- url: `https://bintray.com/api/v1/packages/${subject}/${repo}/${packageName}/versions/_latest`,
- })
- )
- }
-
- async handle({ subject, repo, packageName }) {
- const data = await this.fetch({ subject, repo, packageName })
- return renderVersionBadge({ version: data.name })
- }
-}
diff --git a/services/bintray/bintray.spec.js b/services/bintray/bintray.spec.js
deleted file mode 100644
index 2bb79cd71a84f..0000000000000
--- a/services/bintray/bintray.spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const Bintray = require('./bintray.service')
-
-describe('Bintray', function() {
- describe('auth', function() {
- cleanUpNockAfterEach()
-
- const user = 'admin'
- const pass = 'password'
- const config = {
- private: {
- bintray_user: user,
- bintray_apikey: pass,
- },
- }
-
- it('sends the auth information as configured', async function() {
- const scope = nock('https://bintray.com')
- .get('/api/v1/packages/asciidoctor/maven/asciidoctorj/versions/_latest')
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, {
- name: '1.5.7',
- })
-
- expect(
- await Bintray.invoke(defaultContext, config, {
- subject: 'asciidoctor',
- repo: 'maven',
- packageName: 'asciidoctorj',
- })
- ).to.deep.equal({
- label: undefined,
- message: 'v1.5.7',
- color: 'blue',
- })
-
- scope.done()
- })
- })
-})
diff --git a/services/bintray/bintray.tester.js b/services/bintray/bintray.tester.js
deleted file mode 100644
index e019a5f29d76b..0000000000000
--- a/services/bintray/bintray.tester.js
+++ /dev/null
@@ -1,35 +0,0 @@
-'use strict'
-
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('version')
- .get('/asciidoctor/maven/asciidoctorj.json')
- .expectBadge({
- label: 'bintray',
- message: isVPlusDottedVersionNClausesWithOptionalSuffix,
- })
-
-t.create('version (not found)')
- .get('/asciidoctor/maven/not-a-real-package.json')
- .expectBadge({
- label: 'bintray',
- message: 'not found',
- })
-
-t.create('version (mocked)')
- .get('/asciidoctor/maven/asciidoctorj.json')
- .intercept(nock =>
- nock('https://bintray.com')
- .get('/api/v1/packages/asciidoctor/maven/asciidoctorj/versions/_latest')
- .reply(200, {
- name: '1.5.7',
- })
- )
- .expectBadge({
- label: 'bintray',
- message: 'v1.5.7',
- color: 'blue',
- })
diff --git a/services/bit/bit-components.service.js b/services/bit/bit-components.service.js
index 866f4898bc5ba..60a6e700663f1 100644
--- a/services/bit/bit-components.service.js
+++ b/services/bit/bit-components.service.js
@@ -1,61 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { downloadCount } = require('../color-formatters')
-const { BaseJsonService } = require('..')
-
-const collectionSchema = Joi.object({
- payload: Joi.object({
- totalComponents: nonNegativeInteger,
- }).required(),
-}).required()
-
-module.exports = class BitComponents extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'bit/collection/total-components',
- pattern: ':owner/:collection',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'bit',
- namedParams: { owner: 'ramda', collection: 'ramda' },
- staticPreview: this.render({ count: 330 }),
- keywords: ['components'],
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'components' }
- }
-
- static render({ count }) {
- return { message: metric(count), color: downloadCount(count) }
- }
-
- async fetch({ owner, collection }) {
- const url = `https://api.bit.dev/scope/${owner}/${collection}/`
- return this._requestJson({
- url,
- schema: collectionSchema,
- errorMessages: {
- 404: 'collection not found',
- },
- })
- }
-
- async handle({ owner, collection }) {
- const json = await this.fetch({ owner, collection })
- return this.constructor.render({ count: json.payload.totalComponents })
- }
-}
+import { retiredService } from '../index.js'
+
+export const BitComponents = retiredService({
+ category: 'other',
+ route: {
+ base: 'bit/collection/total-components',
+ pattern: ':owner/:collection',
+ },
+ label: 'bitcomponents',
+ dateAdded: new Date('2025-11-01'),
+})
diff --git a/services/bit/bit-components.tester.js b/services/bit/bit-components.tester.js
index 7fc943384038c..9e64107cceeb9 100644
--- a/services/bit/bit-components.tester.js
+++ b/services/bit/bit-components.tester.js
@@ -1,15 +1,12 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
+export const t = new ServiceTester({
+ id: 'bitcomponents',
+ title: 'BitComponents',
+ pathPrefix: '/bit/collection/total-components',
+})
-t.create('collection (valid)')
- .get('/ramda/ramda.json')
- .expectBadge({
- label: 'components',
- message: isMetric,
- })
-
-t.create('collection (valid)')
- .get('/bit/no-collection-test.json')
- .expectBadge({ label: 'components', message: 'collection not found' })
+t.create('collection').get('/ramda/ramda.json').expectBadge({
+ label: 'bitcomponents',
+ message: 'retired badge',
+})
diff --git a/services/bitbucket/bitbucket-issues.service.js b/services/bitbucket/bitbucket-issues.service.js
index 29500332056f5..58a4b14619926 100644
--- a/services/bitbucket/bitbucket-issues.service.js
+++ b/services/bitbucket/bitbucket-issues.service.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const bitbucketIssuesSchema = Joi.object({
size: nonNegativeInteger,
@@ -12,39 +10,35 @@ const bitbucketIssuesSchema = Joi.object({
function issueClassGenerator(raw) {
const routePrefix = raw ? 'issues-raw' : 'issues'
const badgeSuffix = raw ? '' : ' open'
+ const titleSuffix = raw ? ' (raw)' : ''
return class BitbucketIssues extends BaseJsonService {
- static get name() {
- return `BitbucketIssues${raw ? 'Raw' : ''}`
- }
-
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: `bitbucket/${routePrefix}`,
- pattern: ':user/:repo',
- }
- }
+ static name = `BitbucketIssues${raw ? 'Raw' : ''}`
+ static category = 'issue-tracking'
+ static route = { base: `bitbucket/${routePrefix}`, pattern: ':user/:repo' }
- static get examples() {
- return [
- {
- title: 'Bitbucket open issues',
- namedParams: {
- user: 'atlassian',
- repo: 'python-bitbucket',
- },
- staticPreview: this.render({ issues: 33 }),
+ static get openApi() {
+ const key = `/bitbucket/${routePrefix}/{user}/{repo}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `Bitbucket open issues ${titleSuffix}`,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'shields-io',
+ },
+ {
+ name: 'repo',
+ example: 'test-repo',
+ },
+ ),
},
- ]
+ }
+ return route
}
- static get defaultBadgeData() {
- return { label: 'issues' }
- }
+ static defaultBadgeData = { label: 'issues' }
static render({ issues }) {
return {
@@ -61,9 +55,9 @@ function issueClassGenerator(raw) {
schema: bitbucketIssuesSchema,
// https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering#query-issues
options: {
- qs: { limit: 0, q: '(state = "new" OR state = "open")' },
+ searchParams: { limit: 0, q: '(state = "new" OR state = "open")' },
},
- errorMessages: { 403: 'private repo' },
+ httpErrors: { 403: 'private repo' },
})
}
@@ -74,4 +68,5 @@ function issueClassGenerator(raw) {
}
}
-module.exports = [true, false].map(issueClassGenerator)
+export const BitbucketRawIssues = issueClassGenerator(true)
+export const BitbucketNonRawIssues = issueClassGenerator(false)
diff --git a/services/bitbucket/bitbucket-issues.tester.js b/services/bitbucket/bitbucket-issues.tester.js
index 4f05579b3577a..cdf434ea00b77 100644
--- a/services/bitbucket/bitbucket-issues.tester.js
+++ b/services/bitbucket/bitbucket-issues.tester.js
@@ -1,23 +1,21 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isMetric, isMetricOpenIssues } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOpenIssues } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'BitbucketIssues',
title: 'Bitbucket Issues',
pathPrefix: '/bitbucket',
-}))
+})
t.create('issues-raw (valid)')
- .get('/issues-raw/atlassian/python-bitbucket.json')
+ .get('/issues-raw/shields-io/test-repo.json')
.expectBadge({
label: 'issues',
message: isMetric,
})
t.create('issues-raw (not found)')
- .get('/issues-raw/atlassian/not-a-repo.json')
+ .get('/issues-raw/shields-io/not-a-repo.json')
.expectBadge({ label: 'issues', message: 'not found' })
t.create('issues-raw (private repo)')
@@ -25,14 +23,14 @@ t.create('issues-raw (private repo)')
.expectBadge({ label: 'issues', message: 'private repo' })
t.create('issues (valid)')
- .get('/issues/atlassian/python-bitbucket.json')
+ .get('/issues/shields-io/test-repo.json')
.expectBadge({
label: 'issues',
message: isMetricOpenIssues,
})
t.create('issues (not found)')
- .get('/issues/atlassian/not-a-repo.json')
+ .get('/issues/shields-io/not-a-repo.json')
.expectBadge({ label: 'issues', message: 'not found' })
t.create('issues (private repo)')
diff --git a/services/bitbucket/bitbucket-last-commit.service.js b/services/bitbucket/bitbucket-last-commit.service.js
new file mode 100644
index 0000000000000..45a60ffb07585
--- /dev/null
+++ b/services/bitbucket/bitbucket-last-commit.service.js
@@ -0,0 +1,73 @@
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
+import { relativeUri } from '../validators.js'
+
+const schema = Joi.object({
+ values: Joi.array().items({
+ date: Joi.string().isoDate().required(),
+ }),
+}).required()
+
+const queryParamSchema = Joi.object({
+ path: relativeUri,
+}).required()
+
+export default class BitbucketLastCommit extends BaseJsonService {
+ static category = 'activity'
+ static route = {
+ base: 'bitbucket/last-commit',
+ pattern: ':user/:repo/:branch+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/bitbucket/last-commit/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Bitbucket last commit',
+ parameters: [
+ pathParam({ name: 'user', example: 'shields-io' }),
+ pathParam({ name: 'repo', example: 'test-repo' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last commit' }
+
+ async fetch({ user, repo, branch, path }) {
+ // https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-commits-get
+ return this._requestJson({
+ url: `https://bitbucket.org/api/2.0/repositories/${user}/${repo}/commits/${branch}`,
+ options: {
+ searchParams: {
+ path,
+ pagelen: 1,
+ fields: 'values.date',
+ },
+ },
+ schema,
+ httpErrors: {
+ 403: 'private repo',
+ 404: 'user, repo or branch not found',
+ },
+ })
+ }
+
+ async handle({ user, repo, branch }, queryParams) {
+ const { path } = queryParams
+ const data = await this.fetch({ user, repo, branch, path })
+ const [commit] = data.values
+
+ if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
+
+ return renderDateBadge(commit.date)
+ }
+}
diff --git a/services/bitbucket/bitbucket-last-commit.tester.js b/services/bitbucket/bitbucket-last-commit.tester.js
new file mode 100644
index 0000000000000..8c75524173f45
--- /dev/null
+++ b/services/bitbucket/bitbucket-last-commit.tester.js
@@ -0,0 +1,41 @@
+import { isFormattedDate } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'BitbucketLastCommit',
+ title: 'Bitbucket last commit',
+ pathPrefix: '/bitbucket/last-commit',
+})
+
+t.create('last commit')
+ .get('/shields-io/test-repo/main.json')
+ .expectBadge({ label: 'last commit', message: isFormattedDate })
+
+t.create('last commit (path)')
+ .get('/shields-io/test-repo/main.json?path=README.md')
+ .expectBadge({ label: 'last commit', message: isFormattedDate })
+
+t.create('last commit (user not found)')
+ .get('/not-a-user/test-repo/main.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or branch not found',
+ })
+
+t.create('last commit (repo not found)')
+ .get('/shields-io/not-a-repo/main.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or branch not found',
+ })
+
+t.create('last commit (branch not found)')
+ .get('/shields-io/test-repo/not-a-branch.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or branch not found',
+ })
+
+t.create('last commit (path not found)')
+ .get('/shields-io/test-repo/main.json?path=not/a/dir')
+ .expectBadge({ label: 'last commit', message: 'no commits found' })
diff --git a/services/bitbucket/bitbucket-pipelines.service.js b/services/bitbucket/bitbucket-pipelines.service.js
index 048d44788afbd..85497e01b8d30 100644
--- a/services/bitbucket/bitbucket-pipelines.service.js
+++ b/services/bitbucket/bitbucket-pipelines.service.js
@@ -1,8 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderBuildStatusBadge } from '../build-status.js'
+import {
+ BaseJsonService,
+ redirector,
+ pathParams,
+ InvalidResponse,
+} from '../index.js'
const bitbucketPipelinesSchema = Joi.object({
values: Joi.array()
@@ -16,54 +19,45 @@ const bitbucketPipelinesSchema = Joi.object({
'FAILED',
'ERROR',
'STOPPED',
- 'EXPIRED'
+ 'EXPIRED',
),
- }).required(),
+ }),
}).required(),
- })
+ }),
)
.required(),
}).required()
-module.exports = class BitbucketPipelines extends BaseJsonService {
- static get category() {
- return 'build'
+class BitbucketPipelines extends BaseJsonService {
+ static category = 'build'
+ static route = {
+ base: 'bitbucket/pipelines',
+ pattern: ':user/:repo/:branch+',
}
- static get route() {
- return {
- base: 'bitbucket/pipelines',
- pattern: ':user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Bitbucket Pipelines',
- pattern: ':user/:repo',
- namedParams: {
- user: 'atlassian',
- repo: 'adf-builder-javascript',
- },
- staticPreview: this.render({ status: 'SUCCESSFUL' }),
+ static openApi = {
+ '/bitbucket/pipelines/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Bitbucket Pipelines',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'shields-io',
+ },
+ {
+ name: 'repo',
+ example: 'test-repo',
+ },
+ {
+ name: 'branch',
+ example: 'main',
+ },
+ ),
},
- {
- title: 'Bitbucket Pipelines branch',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'atlassian',
- repo: 'adf-builder-javascript',
- branch: 'task/SECO-2168',
- },
- staticPreview: this.render({ status: 'SUCCESSFUL' }),
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'build' }
- }
+ static defaultBadgeData = { label: 'build' }
static render({ status }) {
return renderBuildStatusBadge({ status: status.toLowerCase() })
@@ -75,7 +69,7 @@ module.exports = class BitbucketPipelines extends BaseJsonService {
url,
schema: bitbucketPipelinesSchema,
options: {
- qs: {
+ searchParams: {
fields: 'values.state',
page: 1,
pagelen: 2,
@@ -84,22 +78,38 @@ module.exports = class BitbucketPipelines extends BaseJsonService {
'target.ref_name': branch,
},
},
- errorMessages: { 403: 'private repo' },
+ httpErrors: { 403: 'private repo' },
})
}
static transform(data) {
const values = data.values.filter(
- value => value.state && value.state.name === 'COMPLETED'
+ value => value.state && value.state.name === 'COMPLETED',
)
if (values.length > 0) {
+ if (!values[0].state?.result?.name) {
+ throw new InvalidResponse({ prettyMessage: 'invalid response data' })
+ }
return values[0].state.result.name
}
return 'never built'
}
async handle({ user, repo, branch }) {
- const data = await this.fetch({ user, repo, branch: branch || 'master' })
+ const data = await this.fetch({ user, repo, branch })
return this.constructor.render({ status: this.constructor.transform(data) })
}
}
+
+const BitbucketPipelinesRedirector = redirector({
+ category: 'build',
+ route: {
+ base: 'bitbucket/pipelines',
+ pattern: ':user/:repo',
+ },
+ transformPath: ({ user, repo }) =>
+ `/bitbucket/pipelines/${user}/${repo}/master`,
+ dateAdded: new Date('2020-07-12'),
+})
+
+export { BitbucketPipelines, BitbucketPipelinesRedirector }
diff --git a/services/bitbucket/bitbucket-pipelines.tester.js b/services/bitbucket/bitbucket-pipelines.tester.js
index 4230a2c562bf0..686d3f46fcb49 100644
--- a/services/bitbucket/bitbucket-pipelines.tester.js
+++ b/services/bitbucket/bitbucket-pipelines.tester.js
@@ -1,7 +1,10 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
+ id: 'BitbucketPipelines',
+ title: 'Bitbucket Pipelines',
+ pathPrefix: '/bitbucket/pipelines',
+})
function bitbucketApiResponse(status) {
return JSON.stringify({
@@ -20,82 +23,112 @@ function bitbucketApiResponse(status) {
})
}
-t.create('master build result (valid)')
- .get('/atlassian/adf-builder-javascript.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
-
-t.create('master build result (not found)')
- .get('/atlassian/not-a-repo.json')
+t.create('main build result (not found)')
+ .get('/shields-io/not-a-repo/main.json')
.expectBadge({ label: 'build', message: 'not found' })
t.create('branch build result (valid)')
- .get('/atlassian/adf-builder-javascript/shields-test-dont-remove.json')
+ .get('/shields-io/test-repo/main.json')
.expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('branch build result (not found)')
- .get('/atlassian/not-a-repo/some-branch.json')
+ .get('/shields-io/not-a-repo/some-branch.json')
.expectBadge({ label: 'build', message: 'not found' })
t.create('branch build result (never built)')
- .get('/atlassian/adf-builder-javascript/some/new/branch.json')
+ .get('/shields-io/test-repo/P-Y/update-readme-1704629121998.json')
.expectBadge({ label: 'build', message: 'never built' })
t.create('build result (passing)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('SUCCESSFUL'))
+ .reply(200, bitbucketApiResponse('SUCCESSFUL')),
)
.expectBadge({ label: 'build', message: 'passing' })
t.create('build result (failing)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('FAILED'))
+ .reply(200, bitbucketApiResponse('FAILED')),
)
.expectBadge({ label: 'build', message: 'failing' })
t.create('build result (error)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('ERROR'))
+ .reply(200, bitbucketApiResponse('ERROR')),
)
.expectBadge({ label: 'build', message: 'error' })
t.create('build result (stopped)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('STOPPED'))
+ .reply(200, bitbucketApiResponse('STOPPED')),
)
.expectBadge({ label: 'build', message: 'stopped' })
t.create('build result (expired)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('EXPIRED'))
+ .reply(200, bitbucketApiResponse('EXPIRED')),
)
.expectBadge({ label: 'build', message: 'expired' })
t.create('build result (unexpected status)')
- .get('/atlassian/adf-builder-javascript.json')
+ .get('/shields-io/test-repo/main.json')
.intercept(nock =>
nock('https://api.bitbucket.org')
.get(/^\/2.0\/.*/)
- .reply(200, bitbucketApiResponse('NEW_AND_UNEXPECTED'))
+ .reply(200, bitbucketApiResponse('NEW_AND_UNEXPECTED')),
)
.expectBadge({ label: 'build', message: 'invalid response data' })
+
+// regression test for https://github.com/badges/shields/issues/10137
+t.create('build result (with manual build steps)')
+ .get('/shields-io/test-repo/main.json')
+ .intercept(nock =>
+ nock('https://api.bitbucket.org')
+ .get(/^\/2.0\/.*/)
+ .reply(200, {
+ values: [
+ {
+ state: {
+ name: 'IN_PROGRESS',
+ type: 'pipeline_state_in_progress',
+ stage: {
+ name: 'PAUSED',
+ type: 'pipeline_state_in_progress_paused',
+ },
+ },
+ },
+ {
+ state: {
+ name: 'COMPLETED',
+ type: 'pipeline_state_completed',
+ result: {
+ name: 'SUCCESSFUL',
+ type: 'pipeline_state_completed_successful',
+ },
+ },
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'build', message: 'passing' })
+
+t.create('build result no branch redirect')
+ .get('/shields-io/test-repo.svg')
+ .expectRedirect('/bitbucket/pipelines/shields-io/test-repo/master.svg')
diff --git a/services/bitbucket/bitbucket-pull-request.service.js b/services/bitbucket/bitbucket-pull-request.service.js
index 9f932aeb6edfc..0e89e1d79c211 100644
--- a/services/bitbucket/bitbucket-pull-request.service.js
+++ b/services/bitbucket/bitbucket-pull-request.service.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { AuthHelper } = require('../../core/base-service/auth-helper')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger, optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { AuthHelper } from '../../core/base-service/auth-helper.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger, optionalUrl } from '../validators.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
const schema = Joi.object({
size: nonNegativeInteger,
@@ -14,7 +12,7 @@ const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-const errorMessages = {
+const httpErrors = {
401: 'invalid credentials',
403: 'private repo',
404: 'not found',
@@ -23,49 +21,45 @@ const errorMessages = {
function pullRequestClassGenerator(raw) {
const routePrefix = raw ? 'pr-raw' : 'pr'
const badgeSuffix = raw ? '' : ' open'
+ const titleSuffix = raw ? ' (raw)' : ''
return class BitbucketPullRequest extends BaseJsonService {
- static get name() {
- return `BitbucketPullRequest${raw ? 'Raw' : ''}`
- }
-
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: `bitbucket/${routePrefix}`,
- pattern: `:user/:repo`,
- queryParamSchema,
- }
+ static name = `BitbucketPullRequest${raw ? 'Raw' : ''}`
+ static category = 'issue-tracking'
+ static route = {
+ base: `bitbucket/${routePrefix}`,
+ pattern: ':user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Bitbucket open pull requests',
- namedParams: {
- user: 'atlassian',
- repo: 'python-bitbucket',
- },
- staticPreview: this.render({ prs: 22 }),
+ static get openApi() {
+ const key = `/bitbucket/${routePrefix}/{user}/{repo}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `Bitbucket open pull requests ${titleSuffix}`,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'shields-io',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'test-repo',
+ }),
+ queryParam({
+ name: 'server',
+ example: 'https://bitbucket.mydomain.net',
+ description:
+ 'When not specified, this will default to `https://bitbucket.org`.',
+ }),
+ ],
},
- {
- title: 'Bitbucket Server open pull requests',
- namedParams: {
- user: 'foo',
- repo: 'bar',
- },
- queryParams: { server: 'https://bitbucket.mydomain.net' },
- staticPreview: this.render({ prs: 42 }),
- },
- ]
+ }
+ return route
}
- static get defaultBadgeData() {
- return { label: 'pull requests' }
- }
+ static defaultBadgeData = { label: 'pull requests' }
static render({ prs }) {
return {
@@ -83,7 +77,7 @@ function pullRequestClassGenerator(raw) {
passKey: 'bitbucket_password',
authorizedOrigins: ['https://bitbucket.org'],
},
- config
+ config,
)
this.bitbucketServerAuthHelper = new AuthHelper(
{
@@ -91,7 +85,7 @@ function pullRequestClassGenerator(raw) {
passKey: 'bitbucket_server_password',
serviceKey: 'bitbucketServer',
},
- config
+ config,
)
}
@@ -100,9 +94,9 @@ function pullRequestClassGenerator(raw) {
this.bitbucketAuthHelper.withBasicAuth({
url: `https://bitbucket.org/api/2.0/repositories/${user}/${repo}/pullrequests/`,
schema,
- options: { qs: { state: 'OPEN', limit: 0 } },
- errorMessages,
- })
+ options: { searchParams: { state: 'OPEN', limit: 0 } },
+ httpErrors,
+ }),
)
}
@@ -113,15 +107,15 @@ function pullRequestClassGenerator(raw) {
url: `${server}/rest/api/1.0/projects/${user}/repos/${repo}/pull-requests`,
schema,
options: {
- qs: {
+ searchParams: {
state: 'OPEN',
limit: 100,
withProperties: false,
withAttributes: false,
},
},
- errorMessages,
- })
+ httpErrors,
+ }),
)
}
@@ -140,4 +134,5 @@ function pullRequestClassGenerator(raw) {
}
}
-module.exports = [true, false].map(pullRequestClassGenerator)
+export const BitbucketRawPullRequests = pullRequestClassGenerator(true)
+export const BitbucketNonRawPullRequests = pullRequestClassGenerator(false)
diff --git a/services/bitbucket/bitbucket-pull-request.spec.js b/services/bitbucket/bitbucket-pull-request.spec.js
index fce1ed1546b0f..ff504d1cd428b 100644
--- a/services/bitbucket/bitbucket-pull-request.spec.js
+++ b/services/bitbucket/bitbucket-pull-request.spec.js
@@ -1,24 +1,22 @@
-'use strict'
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import { BitbucketRawPullRequests } from './bitbucket-pull-request.service.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const [BitbucketPullRequest] = require('./bitbucket-pull-request.service')
-
-describe('BitbucketPullRequest', function() {
+describe('BitbucketPullRequest', function () {
cleanUpNockAfterEach()
const user = 'admin'
const pass = 'password'
- it('Sends auth headers to Bitbucket as configured', async function() {
+ it('Sends auth headers to Bitbucket as configured', async function () {
const scope = nock('https://bitbucket.org/api/2.0/repositories/')
.get(/.*/)
.basicAuth({ user, pass })
.reply(200, { size: 42 })
expect(
- await BitbucketPullRequest.invoke(
+ await BitbucketRawPullRequests.invoke(
defaultContext,
{
public: {
@@ -30,8 +28,8 @@ describe('BitbucketPullRequest', function() {
},
private: { bitbucket_username: user, bitbucket_password: pass },
},
- { user: 'atlassian', repo: 'python-bitbucket' }
- )
+ { user: 'shields-io', repo: 'test-repo' },
+ ),
).to.deep.equal({
message: '42',
color: 'yellow',
@@ -40,14 +38,14 @@ describe('BitbucketPullRequest', function() {
scope.done()
})
- it('Sends auth headers to Bitbucket Server as configured', async function() {
+ it('Sends auth headers to Bitbucket Server as configured', async function () {
const scope = nock('https://bitbucket.example.test/rest/api/1.0/projects')
.get(/.*/)
.basicAuth({ user, pass })
.reply(200, { size: 42 })
expect(
- await BitbucketPullRequest.invoke(
+ await BitbucketRawPullRequests.invoke(
defaultContext,
{
public: {
@@ -63,8 +61,8 @@ describe('BitbucketPullRequest', function() {
},
},
{ user: 'project', repo: 'repo' },
- { server: 'https://bitbucket.example.test' }
- )
+ { server: 'https://bitbucket.example.test' },
+ ),
).to.deep.equal({
message: '42',
color: 'yellow',
diff --git a/services/bitbucket/bitbucket-pull-request.tester.js b/services/bitbucket/bitbucket-pull-request.tester.js
index fe00a95dab5f6..a8e72f103d581 100644
--- a/services/bitbucket/bitbucket-pull-request.tester.js
+++ b/services/bitbucket/bitbucket-pull-request.tester.js
@@ -1,43 +1,39 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isMetric, isMetricOpenIssues } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOpenIssues } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'BitbucketPullRequest',
title: 'Bitbucket Pull Request',
pathPrefix: '/bitbucket',
-}))
+})
t.create('pr-raw (valid)')
- .get('/pr-raw/atlassian/python-bitbucket.json')
+ .get('/pr-raw/shields-io/test-repo.json')
.expectBadge({
label: 'pull requests',
message: isMetric,
})
t.create('pr-raw (not found)')
- .get('/pr-raw/atlassian/not-a-repo.json')
+ .get('/pr-raw/shields-io/not-a-repo.json')
.expectBadge({ label: 'pull requests', message: 'not found' })
t.create('pr-raw (private repo)')
.get('/pr-raw/chris48s/example-private-repo.json')
- .expectBadge({ label: 'pull requests', message: 'private repo' })
+ .expectBadge({ label: 'pull requests', message: 'not found' })
-t.create('pr (valid)')
- .get('/pr/atlassian/python-bitbucket.json')
- .expectBadge({
- label: 'pull requests',
- message: isMetricOpenIssues,
- })
+t.create('pr (valid)').get('/pr/shields-io/test-repo.json').expectBadge({
+ label: 'pull requests',
+ message: isMetricOpenIssues,
+})
t.create('pr (not found)')
- .get('/pr/atlassian/not-a-repo.json')
+ .get('/pr/shields-io/not-a-repo.json')
.expectBadge({ label: 'pull requests', message: 'not found' })
t.create('pr (private repo)')
.get('/pr/chris48s/example-private-repo.json')
- .expectBadge({ label: 'pull requests', message: 'private repo' })
+ .expectBadge({ label: 'pull requests', message: 'not found' })
t.create('pr (server)')
.get('/pr/project/repo.json?server=https://bitbucket.mydomain.net')
@@ -50,7 +46,7 @@ t.create('pr (server)')
withProperties: false,
withAttributes: false,
})
- .reply(200, { size: 42 })
+ .reply(200, { size: 42 }),
)
.expectBadge({
label: 'pull requests',
@@ -68,7 +64,7 @@ t.create('pr (server, invalid credentials)')
withProperties: false,
withAttributes: false,
})
- .reply(401)
+ .reply(401),
)
.expectBadge({
label: 'pull requests',
@@ -86,7 +82,7 @@ t.create('pr (server, private repo)')
withProperties: false,
withAttributes: false,
})
- .reply(403)
+ .reply(403),
)
.expectBadge({
label: 'pull requests',
@@ -104,7 +100,7 @@ t.create('pr (server, not found)')
withProperties: false,
withAttributes: false,
})
- .reply(404)
+ .reply(404),
)
.expectBadge({
label: 'pull requests',
diff --git a/services/bithound/bithound.service.js b/services/bithound/bithound.service.js
deleted file mode 100644
index 0a36660e956a6..0000000000000
--- a/services/bithound/bithound.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'dependencies',
- route: {
- base: 'bithound',
- pattern: ':various*',
- },
- label: 'bithound',
- dateAdded: new Date('2018-07-08'),
-})
diff --git a/services/bithound/bithound.tester.js b/services/bithound/bithound.tester.js
deleted file mode 100644
index f4c70181654b1..0000000000000
--- a/services/bithound/bithound.tester.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'bithound',
- title: 'BitHound',
-}))
-
-t.create('no longer available (code)')
- .get('/code/github/rexxars/sse-channel.json')
- .expectBadge({
- label: 'bithound',
- message: 'no longer available',
- })
-
-t.create('no longer available (dependencies)')
- .get('/dependencies/github/rexxars/sse-channel.json')
- .expectBadge({
- label: 'bithound',
- message: 'no longer available',
- })
-
-t.create('no longer available (devDpendencies)')
- .get('/devDependencies/github/rexxars/sse-channel.json')
- .expectBadge({
- label: 'bithound',
- message: 'no longer available',
- })
diff --git a/services/bitrise/bitrise.service.js b/services/bitrise/bitrise.service.js
index 95b635a4f137a..5ecd2e5a19026 100644
--- a/services/bitrise/bitrise.service.js
+++ b/services/bitrise/bitrise.service.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
// https://devcenter.bitrise.io/api/app-status-badge/
const schema = Joi.object({
@@ -12,35 +10,54 @@ const queryParamSchema = Joi.object({
token: Joi.string().required(),
}).required()
-module.exports = class Bitrise extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'bitrise',
- pattern: ':appId/:branch?',
- queryParamSchema,
- }
+export default class Bitrise extends BaseJsonService {
+ static category = 'build'
+ static route = {
+ base: 'bitrise',
+ pattern: ':appId/:branch?',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Bitrise',
- namedParams: { appId: 'cde737473028420d', branch: 'master' },
- queryParams: { token: 'GCIdEzacE4GW32jLVrZb7A' },
- staticPreview: this.render({ status: 'success' }),
+ static openApi = {
+ '/bitrise/{appId}': {
+ get: {
+ summary: 'Bitrise',
+ parameters: [
+ pathParam({
+ name: 'appId',
+ example: '9fa2e96dc9458fbb',
+ }),
+ queryParam({
+ name: 'token',
+ example: 'abc123def456',
+ required: true,
+ }),
+ ],
},
- ]
+ },
+ '/bitrise/{appId}/{branch}': {
+ get: {
+ summary: 'Bitrise (branch)',
+ parameters: [
+ pathParam({
+ name: 'appId',
+ example: '9fa2e96dc9458fbb',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ queryParam({
+ name: 'token',
+ example: 'abc123def456',
+ required: true,
+ }),
+ ],
+ },
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'bitrise',
- }
- }
+ static defaultBadgeData = { label: 'bitrise' }
static render({ status }) {
const color = {
@@ -64,11 +81,11 @@ module.exports = class Bitrise extends BaseJsonService {
async fetch({ appId, branch, token }) {
return this._requestJson({
url: `https://app.bitrise.io/app/${encodeURIComponent(
- appId
+ appId,
)}/status.json`,
- options: { qs: { token, branch } },
+ options: { searchParams: { token, branch } },
schema,
- errorMessages: {
+ httpErrors: {
403: 'app not found or invalid token',
},
})
diff --git a/services/bitrise/bitrise.tester.js b/services/bitrise/bitrise.tester.js
index b31ba9f78ea99..7623cc8a96c34 100644
--- a/services/bitrise/bitrise.tester.js
+++ b/services/bitrise/bitrise.tester.js
@@ -1,30 +1,29 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('deploy status')
- .get('/cde737473028420d.json?token=GCIdEzacE4GW32jLVrZb7A')
+ .get('/9fa2e96dc9458fbb.json?token=iAJgn0FMJzmMP4ALCi0KdQ')
.expectBadge({
label: 'bitrise',
message: isBuildStatus,
})
t.create('deploy status with branch')
- .get('/cde737473028420d/master.json?token=GCIdEzacE4GW32jLVrZb7A')
+ .get('/9fa2e96dc9458fbb/master.json?token=iAJgn0FMJzmMP4ALCi0KdQ')
.expectBadge({
label: 'bitrise',
message: isBuildStatus,
})
t.create('unknown branch')
- .get('/cde737473028420d/unknown.json?token=GCIdEzacE4GW32jLVrZb7A')
+ .get('/9fa2e96dc9458fbb/unknown.json?token=iAJgn0FMJzmMP4ALCi0KdQ')
.expectBadge({ label: 'bitrise', message: 'branch not found' })
t.create('invalid token')
- .get('/cde737473028420d/unknown.json?token=token')
+ .get('/9fa2e96dc9458fbb/unknown.json?token=token')
.expectBadge({ label: 'bitrise', message: 'app not found or invalid token' })
t.create('invalid App ID')
- .get('/invalid/master.json?token=GCIdEzacE4GW32jLVrZb7A')
+ .get('/invalid/develop.json?token=iAJgn0FMJzmMP4ALCi0KdQ')
.expectBadge({ label: 'bitrise', message: 'app not found or invalid token' })
diff --git a/services/bluesky/bluesky-followers.service.js b/services/bluesky/bluesky-followers.service.js
new file mode 100644
index 0000000000000..db6c5d9c4f698
--- /dev/null
+++ b/services/bluesky/bluesky-followers.service.js
@@ -0,0 +1,56 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ followersCount: nonNegativeInteger,
+}).required()
+
+export default class BlueskyFollowers extends BaseJsonService {
+ static category = 'social'
+ static route = { base: 'bluesky/followers', pattern: ':actor' }
+
+ static openApi = {
+ '/bluesky/followers/{actor}': {
+ get: {
+ summary: 'Bluesky followers',
+ description:
+ 'Displays the number of Bluesky followers for a given handle using the public Bluesky API.',
+ parameters: pathParams({
+ name: 'actor',
+ description: 'Bluesky handle (user ID)',
+ example: 'chitvs.bsky.social',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'followers', namedLogo: 'bluesky' }
+
+ static render({ followers }) {
+ return {
+ message: metric(followers),
+ color: 'blue',
+ style: 'social',
+ }
+ }
+
+ async fetch({ actor }) {
+ return this._requestJson({
+ schema,
+ url: 'https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile',
+ options: {
+ searchParams: { actor },
+ },
+ httpErrors: {
+ 400: 'user not found',
+ },
+ })
+ }
+
+ async handle({ actor }) {
+ const data = await this.fetch({ actor })
+ return this.constructor.render({ followers: data.followersCount })
+ }
+}
diff --git a/services/bluesky/bluesky-followers.tester.js b/services/bluesky/bluesky-followers.tester.js
new file mode 100644
index 0000000000000..17aaf9b6ff248
--- /dev/null
+++ b/services/bluesky/bluesky-followers.tester.js
@@ -0,0 +1,30 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Followers (live)').get('/chitvs.bsky.social.json').expectBadge({
+ label: 'followers',
+ message: isMetric,
+})
+
+t.create('User not found')
+ .get('/this-user-should-not-exist-xyz123.json')
+ .expectBadge({
+ label: 'followers',
+ message: 'user not found',
+ })
+
+t.create('Handles valid numeric response')
+ .get('/mocked-user.json')
+ .intercept(nock =>
+ nock('https://public.api.bsky.app')
+ .get('/xrpc/app.bsky.actor.getProfile')
+ .query({ actor: 'mocked-user' })
+ .reply(200, { followersCount: 9876 }),
+ )
+ .expectBadge({
+ label: 'followers',
+ message: '9.9k',
+ color: 'blue',
+ })
diff --git a/services/bluesky/bluesky-posts.service.js b/services/bluesky/bluesky-posts.service.js
new file mode 100644
index 0000000000000..146c8c07351d0
--- /dev/null
+++ b/services/bluesky/bluesky-posts.service.js
@@ -0,0 +1,56 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ postsCount: nonNegativeInteger,
+}).required()
+
+export default class BlueskyPosts extends BaseJsonService {
+ static category = 'social'
+ static route = { base: 'bluesky/posts', pattern: ':actor' }
+
+ static openApi = {
+ '/bluesky/posts/{actor}': {
+ get: {
+ summary: 'Bluesky posts',
+ description:
+ 'Displays the number of Bluesky posts for a given handle using the public Bluesky API.',
+ parameters: pathParams({
+ name: 'actor',
+ description: 'Bluesky handle (user ID)',
+ example: 'chitvs.bsky.social',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'posts', namedLogo: 'bluesky' }
+
+ static render({ posts }) {
+ return {
+ message: metric(posts),
+ color: 'blue',
+ style: 'social',
+ }
+ }
+
+ async fetch({ actor }) {
+ return this._requestJson({
+ schema,
+ url: 'https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile',
+ options: {
+ searchParams: { actor },
+ },
+ httpErrors: {
+ 400: 'user not found',
+ },
+ })
+ }
+
+ async handle({ actor }) {
+ const data = await this.fetch({ actor })
+ return this.constructor.render({ posts: data.postsCount })
+ }
+}
diff --git a/services/bluesky/bluesky-posts.tester.js b/services/bluesky/bluesky-posts.tester.js
new file mode 100644
index 0000000000000..3bd1352d737e7
--- /dev/null
+++ b/services/bluesky/bluesky-posts.tester.js
@@ -0,0 +1,33 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Posts (live)')
+ .get('/chitvs.bsky.social.json')
+ .expectBadge({
+ label: 'posts',
+ message: Joi.alternatives(isMetric, Joi.equal('0')),
+ })
+
+t.create('User not found')
+ .get('/this-user-should-not-exist-xyz123.json')
+ .expectBadge({
+ label: 'posts',
+ message: 'user not found',
+ })
+
+t.create('Handles valid numeric response')
+ .get('/mocked-user.json')
+ .intercept(nock =>
+ nock('https://public.api.bsky.app')
+ .get('/xrpc/app.bsky.actor.getProfile')
+ .query({ actor: 'mocked-user' })
+ .reply(200, { postsCount: 4321 }),
+ )
+ .expectBadge({
+ label: 'posts',
+ message: '4.3k',
+ color: 'blue',
+ })
diff --git a/services/bountysource/bountysource.service.js b/services/bountysource/bountysource.service.js
deleted file mode 100644
index 75920176ff470..0000000000000
--- a/services/bountysource/bountysource.service.js
+++ /dev/null
@@ -1,58 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({ activity_total: nonNegativeInteger })
-
-module.exports = class Bountysource extends BaseJsonService {
- static get category() {
- return 'funding'
- }
-
- static get route() {
- return {
- base: 'bountysource/team',
- pattern: ':team/activity',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Bountysource',
- namedParams: { team: 'mozilla-core' },
- staticPreview: this.render({ total: 53000 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'bounties' }
- }
-
- static render({ total }) {
- return {
- message: metric(total),
- color: 'brightgreen',
- }
- }
-
- async fetch({ team }) {
- const url = `https://api.bountysource.com/teams/${team}`
- return this._requestJson({
- schema,
- url,
- options: {
- headers: { Accept: 'application/vnd.bountysource+json; version=2' },
- },
- })
- }
-
- async handle({ team }) {
- const json = await this.fetch({ team })
- return this.constructor.render({ total: json.activity_total })
- }
-}
diff --git a/services/bountysource/bountysource.tester.js b/services/bountysource/bountysource.tester.js
deleted file mode 100644
index 634b8adbbc764..0000000000000
--- a/services/bountysource/bountysource.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'bountysource',
- title: 'Bountysource',
-}))
-
-t.create('bounties (valid)')
- .get('/team/mozilla-core/activity.json')
- .expectBadge({
- label: 'bounties',
- message: isMetric,
- })
-
-t.create('bounties (invalid team)')
- .get('/team/not-a-real-team/activity.json')
- .expectBadge({
- label: 'bounties',
- message: 'not found',
- })
diff --git a/services/bower/bower-base.js b/services/bower/bower-base.js
index 793102d70a631..3b059b3b77d05 100644
--- a/services/bower/bower-base.js
+++ b/services/bower/bower-base.js
@@ -1,36 +1,28 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import LibrariesIoBase from '../librariesio/librariesio-base.js'
const schema = Joi.object()
.keys({
normalized_licenses: Joi.array()
.items(
// normalized_license may be [] if the package does not declare a license
- Joi.string()
+ Joi.string(),
)
.required(),
- // latest_stable_release can be object or NULL
- latest_stable_release: Joi.object()
- .keys({
- name: Joi.string().required(),
- })
- .allow(null),
-
- // latest_release_number can be NULL for bower because bower
+ // Keys can be NULL for bower because bower
// has no registry to enforce any release exists
latest_release_number: Joi.string().allow(null),
+ latest_stable_release_number: Joi.string().allow(null),
})
.required()
-module.exports = class BaseBowerService extends BaseJsonService {
+export default class BaseBowerService extends LibrariesIoBase {
async fetch({ packageName }) {
return this._requestJson({
schema,
- url: `https://libraries.io/api/bower/${packageName}`,
- errorMessages: {
+ url: `/bower/${packageName}`,
+ httpErrors: {
404: 'package not found',
},
})
diff --git a/services/bower/bower-license.service.js b/services/bower/bower-license.service.js
index e82e388704144..f3c5941af98f1 100644
--- a/services/bower/bower-license.service.js
+++ b/services/bower/bower-license.service.js
@@ -1,33 +1,24 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import BaseBowerService from './bower-base.js'
-const { renderLicenseBadge } = require('../licenses')
-const BaseBowerService = require('./bower-base')
+export default class BowerLicense extends BaseBowerService {
+ static category = 'license'
+ static route = { base: 'bower/l', pattern: ':packageName' }
-module.exports = class BowerLicense extends BaseBowerService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'bower/l',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Bower',
- namedParams: { packageName: 'bootstrap' },
- staticPreview: renderLicenseBadge({ licenses: ['MIT'] }),
+ static openApi = {
+ '/bower/l/{packageName}': {
+ get: {
+ summary: 'Bower License',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'bootstrap',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
async handle({ packageName }) {
const data = await this.fetch({ packageName })
diff --git a/services/bower/bower-license.tester.js b/services/bower/bower-license.tester.js
index dc89bdaf96279..6f895cfed0a6b 100644
--- a/services/bower/bower-license.tester.js
+++ b/services/bower/bower-license.tester.js
@@ -1,21 +1,11 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('licence')
.timeout(10000)
.get('/bootstrap.json')
.expectBadge({ label: 'license', message: 'MIT' })
-t.create('license not declared')
- .get('/bootstrap.json')
- .intercept(nock =>
- nock('https://libraries.io')
- .get('/api/bower/bootstrap')
- .reply(200, { normalized_licenses: [] })
- )
- .expectBadge({ label: 'license', message: 'missing' })
-
t.create('licence for Invalid Package')
.timeout(10000)
.get('/it-is-a-invalid-package-should-error.json')
diff --git a/services/bower/bower-version.service.js b/services/bower/bower-version.service.js
index 77b304b4814ac..ac335c3190cb2 100644
--- a/services/bower/bower-version.service.js
+++ b/services/bower/bower-version.service.js
@@ -1,75 +1,39 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { InvalidResponse, redirector } = require('..')
-const BaseBowerService = require('./bower-base')
+import { renderVersionBadge } from '../version.js'
+import { InvalidResponse, pathParams } from '../index.js'
+import BaseBowerService from './bower-base.js'
+
+export default class BowerVersion extends BaseBowerService {
+ static category = 'version'
+ static route = { base: 'bower/v', pattern: ':packageName' }
+
+ static openApi = {
+ '/bower/v/{packageName}': {
+ get: {
+ summary: 'Bower Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'bootstrap',
+ }),
+ },
+ },
+ }
-const queryParamSchema = Joi.object({
- include_prereleases: Joi.equal(''),
-}).required()
+ static defaultBadgeData = { label: 'bower' }
-class BowerVersion extends BaseBowerService {
- static get category() {
- return 'version'
- }
+ static transform(data) {
+ const version = data.latest_release_number
- static get route() {
- return {
- base: 'bower/v',
- pattern: ':packageName',
- queryParamSchema,
+ if (!version) {
+ throw new InvalidResponse({ prettyMessage: 'no releases' })
}
- }
-
- static get examples() {
- return [
- {
- title: 'Bower Version',
- namedParams: { packageName: 'bootstrap' },
- staticPreview: renderVersionBadge({ version: '4.2.1' }),
- },
- {
- title: 'Bower Version (including pre-releases)',
- namedParams: { packageName: 'bootstrap' },
- queryParams: { include_prereleases: null },
- staticPreview: renderVersionBadge({ version: '4.2.1' }),
- },
- ]
- }
- static get defaultBadgeData() {
- return { label: 'bower' }
+ return version
}
- async handle({ packageName }, queryParams) {
+ async handle({ packageName }) {
const data = await this.fetch({ packageName })
- const includePrereleases = queryParams.include_prereleases !== undefined
+ const version = this.constructor.transform(data)
- if (includePrereleases) {
- if (data.latest_release_number) {
- return renderVersionBadge({ version: data.latest_release_number })
- }
- } else {
- if (data.latest_stable_release) {
- return renderVersionBadge({ version: data.latest_stable_release.name })
- }
- }
- throw new InvalidResponse({ prettyMessage: 'no releases' })
+ return renderVersionBadge({ version })
}
}
-
-const BowerVersionRedirect = redirector({
- category: 'version',
- route: {
- base: 'bower/vpre',
- pattern: ':packageName',
- },
- transformPath: ({ packageName }) => `/bower/v/${packageName}`,
- transformQueryParams: params => ({
- include_prereleases: null,
- }),
- dateAdded: new Date('2019-12-15'),
-})
-
-module.exports = { BowerVersion, BowerVersionRedirect }
diff --git a/services/bower/bower-version.spec.js b/services/bower/bower-version.spec.js
new file mode 100644
index 0000000000000..28c65623bde7e
--- /dev/null
+++ b/services/bower/bower-version.spec.js
@@ -0,0 +1,72 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import { InvalidResponse } from '../index.js'
+import LibrariesIoApiProvider from '../librariesio/librariesio-api-provider.js'
+import BowerVersion from './bower-version.service.js'
+
+describe('BowerVersion', function () {
+ test(BowerVersion.transform, () => {
+ given({
+ latest_release_number: '2.0.0-beta',
+ latest_stable_release_number: '1.8.3',
+ }).expect('2.0.0-beta')
+ })
+
+ it('throws `no releases` InvalidResponse if no stable version', function () {
+ expect(() => BowerVersion.transform({}))
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'no releases')
+ })
+
+ context('auth', function () {
+ cleanUpNockAfterEach()
+ const fakeApiKey = 'fakeness'
+ const response = {
+ normalized_licenses: [],
+ latest_release_number: '2.0.0-beta',
+ latest_stable_release_number: '1.8.3',
+ }
+ const config = {
+ private: {
+ librariesio_tokens: fakeApiKey,
+ },
+ }
+ const librariesIoApiProvider = new LibrariesIoApiProvider({
+ baseUrl: 'https://libraries.io/api',
+ tokens: [fakeApiKey],
+ })
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://libraries.io/api')
+ // This ensures that the expected credentials are actually being sent with the HTTP request.
+ // Without this the request wouldn't match and the test would fail.
+ .get(`/bower/bootstrap?api_key=${fakeApiKey}`)
+ .reply(200, response)
+
+ expect(
+ await BowerVersion.invoke(
+ {
+ ...defaultContext,
+ librariesIoApiProvider,
+ },
+ config,
+ {
+ platform: 'bower',
+ packageName: 'bootstrap',
+ },
+ {
+ include_prereleases: '',
+ },
+ ),
+ ).to.deep.equal({
+ message: 'v2.0.0-beta',
+ color: 'orange',
+ label: undefined,
+ })
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/bower/bower-version.tester.js b/services/bower/bower-version.tester.js
index 258c61d0fb356..2c5d1c4d5429e 100644
--- a/services/bower/bower-version.tester.js
+++ b/services/bower/bower-version.tester.js
@@ -1,32 +1,25 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import {
+ isVPlusDottedVersionAtLeastOne,
+ isVPlusDottedVersionNClausesWithOptionalSuffix,
+} from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'BowerVersion',
title: 'Bower Version',
pathPrefix: '/bower',
-}))
+})
-const isBowerPrereleaseVersion = Joi.string().regex(
- /^v\d+(\.\d+)?(\.\d+)?(-?[.\w\d])+?$/
-)
+t.create('version').timeout(10000).get('/v/backbone.json').expectBadge({
+ label: 'bower',
+ message: isVPlusDottedVersionAtLeastOne,
+})
-t.create('version')
+t.create('pre version')
.timeout(10000)
- .get('/v/bootstrap.json')
+ .get('/v/angular.json?include_prereleases')
.expectBadge({
label: 'bower',
- message: isVPlusDottedVersionAtLeastOne,
- })
-
-t.create('pre version') // e.g. bower|v0.2.5-alpha-rc-pre
- .timeout(10000)
- .get('/v/bootstrap.json?include_prereleases')
- .expectBadge({
- label: 'bower',
- message: isBowerPrereleaseVersion,
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
t.create('Version for Invalid Package')
@@ -38,25 +31,3 @@ t.create('Pre Version for Invalid Package')
.timeout(10000)
.get('/v/it-is-a-invalid-package-should-error.json?include_prereleases')
.expectBadge({ label: 'bower', message: 'package not found' })
-
-t.create('Version label should be `no releases` if no stable version')
- .get('/v/bootstrap.json')
- .intercept(nock =>
- nock('https://libraries.io')
- .get('/api/bower/bootstrap')
- .reply(200, { normalized_licenses: [], latest_stable_release: null })
- )
- .expectBadge({ label: 'bower', message: 'no releases' })
-
-t.create('Version label should be `no releases` if no pre-release')
- .get('/v/bootstrap.json?include_prereleases')
- .intercept(nock =>
- nock('https://libraries.io')
- .get('/api/bower/bootstrap')
- .reply(200, { normalized_licenses: [], latest_release_number: null })
- )
- .expectBadge({ label: 'bower', message: 'no releases' })
-
-t.create('Version (legacy redirect: vpre)')
- .get('/vpre/bootstrap.svg')
- .expectRedirect('/bower/v/bootstrap.svg?include_prereleases')
diff --git a/services/bstats/bstats-players.service.js b/services/bstats/bstats-players.service.js
index 0caf2ab2b2b51..83175235a2ff1 100644
--- a/services/bstats/bstats-players.service.js
+++ b/services/bstats/bstats-players.service.js
@@ -1,43 +1,28 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.array()
.items(Joi.array().items(Joi.number().required(), Joi.number().required()))
.required()
-module.exports = class BStatsPlayers extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'bstats/players',
- pattern: ':pluginid',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'bStats Players',
- namedParams: {
- pluginid: '1',
- },
- staticPreview: this.render({ players: 74299 }),
+export default class BStatsPlayers extends BaseJsonService {
+ static category = 'other'
+ static route = { base: 'bstats/players', pattern: ':pluginid' }
+
+ static openApi = {
+ '/bstats/players/{pluginid}': {
+ get: {
+ summary: 'bStats Players',
+ parameters: pathParams({
+ name: 'pluginid',
+ example: '1',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'players',
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'players', color: 'blue' }
static render({ players }) {
return {
@@ -51,7 +36,7 @@ module.exports = class BStatsPlayers extends BaseJsonService {
return this._requestJson({
schema,
options: {
- qs: {
+ searchParams: {
maxElements: 1,
},
},
diff --git a/services/bstats/bstats-players.tester.js b/services/bstats/bstats-players.tester.js
index e7d6d1995b782..31b84be9fd645 100644
--- a/services/bstats/bstats-players.tester.js
+++ b/services/bstats/bstats-players.tester.js
@@ -1,11 +1,8 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Players')
- .get('/1.json')
- .expectBadge({
- label: 'players',
- message: isMetric,
- })
+t.create('Players').get('/1.json').expectBadge({
+ label: 'players',
+ message: isMetric,
+})
diff --git a/services/bstats/bstats-servers.service.js b/services/bstats/bstats-servers.service.js
index a3ae2f3e30527..7b642ad1dbb6d 100644
--- a/services/bstats/bstats-servers.service.js
+++ b/services/bstats/bstats-servers.service.js
@@ -1,43 +1,28 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.array()
.items(Joi.array().items(Joi.number().required(), Joi.number().required()))
.required()
-module.exports = class BStatsServers extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'bstats/servers',
- pattern: ':pluginid',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'bStats Servers',
- namedParams: {
- pluginid: '1',
- },
- staticPreview: this.render({ servers: 57479 }),
+export default class BStatsServers extends BaseJsonService {
+ static category = 'other'
+ static route = { base: 'bstats/servers', pattern: ':pluginid' }
+
+ static openApi = {
+ '/bstats/servers/{pluginid}': {
+ get: {
+ summary: 'bStats Servers',
+ parameters: pathParams({
+ name: 'pluginid',
+ example: '1',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'servers',
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'servers', color: 'blue' }
static render({ servers }) {
return {
@@ -51,7 +36,7 @@ module.exports = class BStatsServers extends BaseJsonService {
return this._requestJson({
schema,
options: {
- qs: {
+ searchParams: {
maxElements: 1,
},
},
diff --git a/services/bstats/bstats-servers.tester.js b/services/bstats/bstats-servers.tester.js
index b4be985b68502..c673891b67cc6 100644
--- a/services/bstats/bstats-servers.tester.js
+++ b/services/bstats/bstats-servers.tester.js
@@ -1,11 +1,8 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Servers')
- .get('/1.json')
- .expectBadge({
- label: 'servers',
- message: isMetric,
- })
+t.create('Servers').get('/1.json').expectBadge({
+ label: 'servers',
+ message: isMetric,
+})
diff --git a/services/bugzilla/bugzilla.service.js b/services/bugzilla/bugzilla.service.js
index b7473c2138320..b03adf4f43c54 100644
--- a/services/bugzilla/bugzilla.service.js
+++ b/services/bugzilla/bugzilla.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
const queryParamSchema = Joi.object({
baseUrl: optionalUrl,
@@ -13,67 +11,44 @@ const schema = Joi.object({
.items(
Joi.object({
status: Joi.string().required(),
- resolution: Joi.string()
- .allow('')
- .required(),
- }).required()
+ resolution: Joi.string().allow('').required(),
+ }).required(),
)
.min(1)
.required(),
}).required()
-const documentation = `
-
- If your Bugzilla badge errors, it might be because you are trying to load a private bug.
-
+const description = `
+Use the baseUrl query parameter to target different Bugzilla deployments.
+If your Bugzilla badge errors, it might be because you are trying to load a private bug.
`
-module.exports = class Bugzilla extends BaseJsonService {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'bugzilla',
- pattern: ':bugNumber',
- queryParamSchema,
- }
- }
+export default class Bugzilla extends BaseJsonService {
+ static category = 'issue-tracking'
+ static route = { base: 'bugzilla', pattern: ':bugNumber', queryParamSchema }
- static get examples() {
- return [
- {
- title: 'Bugzilla bug status (Mozilla)',
- namedParams: {
- bugNumber: '996038',
- },
- staticPreview: this.render({
- bugNumber: 996038,
- status: 'FIXED',
- resolution: '',
- }),
- documentation,
+ static openApi = {
+ '/bugzilla/{bugNumber}': {
+ get: {
+ summary: 'Bugzilla bug status',
+ description,
+ parameters: [
+ pathParam({
+ name: 'bugNumber',
+ example: '12345',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://gcc.gnu.org/bugzilla',
+ description:
+ 'When not specified, this will default to `https://bugzilla.mozilla.org`.',
+ }),
+ ],
},
- {
- title: 'Bugzilla bug status (non-Mozilla)',
- namedParams: {
- bugNumber: '545424',
- },
- queryParams: { baseUrl: 'https://bugs.eclipse.org/bugs' },
- staticPreview: this.render({
- bugNumber: 545424,
- status: 'RESOLVED',
- resolution: 'FIXED',
- }),
- documentation,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'bugzilla' }
- }
+ static defaultBadgeData = { label: 'bugzilla' }
static getDisplayStatus({ status, resolution }) {
let displayStatus =
diff --git a/services/bugzilla/bugzilla.spec.js b/services/bugzilla/bugzilla.spec.js
index 89453c32ae5f0..1baa9eb207cf8 100644
--- a/services/bugzilla/bugzilla.spec.js
+++ b/services/bugzilla/bugzilla.spec.js
@@ -1,13 +1,11 @@
-'use strict'
+import { test, given } from 'sazerac'
+import Bugzilla from './bugzilla.service.js'
-const { test, given } = require('sazerac')
-const Bugzilla = require('./bugzilla.service')
-
-describe('getDisplayStatus function', function() {
- it('formats status correctly', async function() {
+describe('getDisplayStatus function', function () {
+ it('formats status correctly', async function () {
test(Bugzilla.getDisplayStatus, () => {
given({ status: 'RESOLVED', resolution: 'WORKSFORME' }).expect(
- 'works for me'
+ 'works for me',
)
given({ status: 'RESOLVED', resolution: 'WONTFIX' }).expect("won't fix")
given({ status: 'ASSIGNED', resolution: '' }).expect('assigned')
diff --git a/services/bugzilla/bugzilla.tester.js b/services/bugzilla/bugzilla.tester.js
index de79edcd6821b..da627f7c53cfa 100644
--- a/services/bugzilla/bugzilla.tester.js
+++ b/services/bugzilla/bugzilla.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const bzBugStatus = Joi.equal(
'unconfirmed',
@@ -12,23 +11,21 @@ const bzBugStatus = Joi.equal(
"won't fix",
'duplicate',
'works for me',
- 'incomplete'
+ 'incomplete',
)
-t.create('Bugzilla valid bug status')
- .get('/996038.json')
- .expectBadge({
- label: 'bug 996038',
- message: bzBugStatus,
- })
+t.create('Bugzilla valid bug status').get('/996038.json').expectBadge({
+ label: 'bug 996038',
+ message: bzBugStatus,
+})
t.create('Bugzilla valid bug status with custom baseUrl')
- .get('/545424.json?baseUrl=https://bugs.eclipse.org/bugs')
+ .get('/12345.json?baseUrl=https://gcc.gnu.org/bugzilla')
.expectBadge({
- label: 'bug 545424',
+ label: 'bug 12345',
message: bzBugStatus,
})
t.create('Bugzilla invalid bug status')
- .get('/102.json?baseUrl=https://bugzilla.gnome.org')
+ .get('/001.json')
.expectBadge({ label: 'bugzilla', message: 'not found' })
diff --git a/services/build-status.js b/services/build-status.js
index baa620371dc2a..c48742536b65f 100644
--- a/services/build-status.js
+++ b/services/build-status.js
@@ -1,6 +1,10 @@
-'use strict'
+/**
+ * Common functions and schemas for tasks related to build status.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
const greenStatuses = [
'fixed',
@@ -14,7 +18,9 @@ const greenStatuses = [
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
const redStatuses = [
+ 'broken',
'error',
+ 'errored',
'failed',
'failing',
'failure',
@@ -22,9 +28,11 @@ const redStatuses = [
]
const otherStatuses = [
+ 'aborted',
'building',
'canceled',
'cancelled',
+ 'created',
'expired',
'initiated',
'no builds',
@@ -48,8 +56,23 @@ const allStatuses = greenStatuses
.concat(redStatuses)
.concat(otherStatuses)
+/**
+ * Joi schema for validating Build Status.
+ * Checks if the build status is present in the list of allowed build status.
+ *
+ * @type {Joi}
+ */
const isBuildStatus = Joi.equal(...allStatuses)
+/**
+ * Handles rendering concerns of badges that display build status.
+ * Determines the message and color of the badge according to the build status.
+ *
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} [attrs.label] If provided then badge label is set to this value
+ * @param {string} attrs.status Build status
+ * @returns {object} Badge with label, message and color properties
+ */
function renderBuildStatusBadge({ label, status }) {
let message
let color
@@ -72,4 +95,4 @@ function renderBuildStatusBadge({ label, status }) {
}
}
-module.exports = { isBuildStatus, renderBuildStatusBadge }
+export { isBuildStatus, renderBuildStatusBadge }
diff --git a/services/build-status.spec.js b/services/build-status.spec.js
index 69b19d2165599..9a06fd2730c5b 100644
--- a/services/build-status.spec.js
+++ b/services/build-status.spec.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const { expect } = require('chai')
-const { test, given, forCases } = require('sazerac')
-const { renderBuildStatusBadge } = require('./build-status')
+import { expect } from 'chai'
+import { test, given, forCases } from 'sazerac'
+import { renderBuildStatusBadge } from './build-status.js'
test(renderBuildStatusBadge, () => {
given({ label: 'build', status: 'passed' }).expect({
@@ -41,7 +39,7 @@ test(renderBuildStatusBadge, () => {
given({ status: 'success' }),
given({ status: 'successful' }),
]).assert('should be brightgreen', b =>
- expect(b).to.include({ color: 'brightgreen' })
+ expect(b).to.include({ color: 'brightgreen' }),
)
})
@@ -55,7 +53,9 @@ test(renderBuildStatusBadge, () => {
test(renderBuildStatusBadge, () => {
forCases([
+ given({ status: 'broken' }),
given({ status: 'error' }),
+ given({ status: 'errored' }),
given({ status: 'failed' }),
given({ status: 'failing' }),
given({ status: 'failure' }),
@@ -85,6 +85,6 @@ test(renderBuildStatusBadge, () => {
given({ status: 'testing' }),
given({ status: 'waiting' }),
]).assert('should have undefined color', b =>
- expect(b).to.include({ color: undefined })
+ expect(b).to.include({ color: undefined }),
)
})
diff --git a/services/buildbot/buildbot.service.js b/services/buildbot/buildbot.service.js
new file mode 100644
index 0000000000000..2089ae8f094a3
--- /dev/null
+++ b/services/buildbot/buildbot.service.js
@@ -0,0 +1,112 @@
+import Joi from 'joi'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
+
+// Buildbot result codes:
+// https://docs.buildbot.net/latest/developer/results.html
+// SUCCESS=0, WARNINGS=1, FAILURE=2, SKIPPED=3, EXCEPTION=4, RETRY=5, CANCELLED=6
+const resultMap = {
+ 0: 'passing',
+ 1: 'unstable',
+ 2: 'failing',
+ 3: 'skipped',
+ 4: 'failing',
+ 5: 'building',
+ 6: 'cancelled',
+}
+
+const schema = Joi.object({
+ builds: Joi.array()
+ .items(
+ Joi.object({
+ complete: Joi.boolean().required(),
+ results: Joi.number()
+ .integer()
+ .valid(...Object.keys(resultMap).map(Number))
+ .allow(null),
+ }),
+ )
+ .required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ baseUrl: Joi.string()
+ .uri({ scheme: ['http', 'https'] })
+ .required(),
+}).required()
+
+const description = `
+Shows the build status of the most recent build for a
+[Buildbot](https://buildbot.net/) builder.
+
+Each Buildbot installation has its own URL (for example
+\`https://buildbot.mariadb.org\`).
+`
+
+export default class Buildbot extends BaseJsonService {
+ static category = 'build'
+
+ static route = {
+ base: 'buildbot/build',
+ pattern: ':builder',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/buildbot/build/{builder}': {
+ get: {
+ summary: 'Buildbot',
+ description,
+ parameters: [
+ pathParam({
+ name: 'builder',
+ example: 'amd64-rhel8-dockerlibrary',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://buildbot.mariadb.org',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'build' }
+
+ static render({ status }) {
+ return renderBuildStatusBadge({ status })
+ }
+
+ async fetch({ baseUrl, builder }) {
+ return this._requestJson({
+ schema,
+ url: `${baseUrl}/api/v2/builders/${encodeURIComponent(builder)}/builds`,
+ options: {
+ searchParams: { limit: '1', order: '-number' },
+ },
+ httpErrors: {
+ 404: 'builder not found',
+ },
+ })
+ }
+
+ transform({ builds }) {
+ if (builds.length === 0) {
+ throw new NotFound({ prettyMessage: 'no builds found' })
+ }
+
+ const { complete, results } = builds[0]
+ if (!complete) {
+ return { status: 'building' }
+ }
+
+ return { status: resultMap[results] ?? 'unknown' }
+ }
+
+ async handle({ builder }, { baseUrl }) {
+ const json = await this.fetch({ baseUrl, builder })
+ const { status } = this.transform({ builds: json.builds })
+ return this.constructor.render({ status })
+ }
+}
diff --git a/services/buildbot/buildbot.tester.js b/services/buildbot/buildbot.tester.js
new file mode 100644
index 0000000000000..b47de32b0ade9
--- /dev/null
+++ b/services/buildbot/buildbot.tester.js
@@ -0,0 +1,14 @@
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('buildbot (valid)')
+ .get('/amd64-rhel8-dockerlibrary.json?baseUrl=https://buildbot.mariadb.org')
+ .expectBadge({
+ label: 'build',
+ message: isBuildStatus,
+ })
+
+t.create('buildbot (no builds)')
+ .get('/does-not-exist.json?baseUrl=https://buildbot.mariadb.org')
+ .expectBadge({ label: 'build', message: 'no builds found' })
diff --git a/services/buildkite/buildkite.service.js b/services/buildkite/buildkite.service.js
index efbd9b8503d8e..c9612b6e3c3cb 100644
--- a/services/buildkite/buildkite.service.js
+++ b/services/buildkite/buildkite.service.js
@@ -1,55 +1,48 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { BaseJsonService, pathParams } from '../index.js'
// unknown is a valid 'other' status for Buildkite
const schema = Joi.object({
status: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
}).required()
-module.exports = class Buildkite extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'buildkite',
- pattern: ':identifier/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Buildkite',
- pattern: ':identifier',
- namedParams: {
- identifier: '3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+export default class Buildkite extends BaseJsonService {
+ static category = 'build'
+ static route = { base: 'buildkite', pattern: ':identifier/:branch*' }
+
+ static openApi = {
+ '/buildkite/{identifier}': {
+ get: {
+ summary: 'Buildkite',
+ parameters: pathParams({
+ name: 'identifier',
+ example: '3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489',
+ }),
},
- {
- title: 'Buildkite (branch)',
- pattern: ':identifier/:branch',
- namedParams: {
- identifier: '3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+ },
+ '/buildkite/{identifier}/{branch}': {
+ get: {
+ summary: 'Buildkite (branch)',
+ parameters: pathParams(
+ {
+ name: 'identifier',
+ example: '3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'build' }
- }
+ static defaultBadgeData = { label: 'build' }
async fetch({ identifier, branch }) {
const url = `https://badge.buildkite.com/${identifier}.json`
- const options = { qs: { branch } }
+ const options = { searchParams: { branch } }
return this._requestJson({
schema,
url,
diff --git a/services/buildkite/buildkite.tester.js b/services/buildkite/buildkite.tester.js
index fe15e3d088a12..fac5ea4751725 100644
--- a/services/buildkite/buildkite.tester.js
+++ b/services/buildkite/buildkite.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('buildkite invalid pipeline')
.get('/unknown-identifier/unknown-branch.json')
@@ -24,6 +23,6 @@ t.create('buildkite valid pipeline skipping branch')
t.create('buildkite unknown branch')
.get(
- '/3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489/unknown-branch.json'
+ '/3826789cf8890b426057e6fe1c4e683bdf04fa24d498885489/unknown-branch.json',
)
.expectBadge({ label: 'build', message: 'unknown' })
diff --git a/services/bundlejs/bundlejs-package.service.js b/services/bundlejs/bundlejs-package.service.js
new file mode 100644
index 0000000000000..56170387b5507
--- /dev/null
+++ b/services/bundlejs/bundlejs-package.service.js
@@ -0,0 +1,158 @@
+import Joi from 'joi'
+import byteSize from 'byte-size'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { renderSizeBadge } from '../size.js'
+import { nonNegativeInteger } from '../validators.js'
+
+const schema = Joi.object({
+ size: Joi.object({
+ rawCompressedSize: nonNegativeInteger,
+ rawUncompressedSize: nonNegativeInteger,
+ }).required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ exports: Joi.string(),
+ externals: Joi.string(),
+ format: Joi.string().valid('min', 'minzip', 'both'),
+}).required()
+
+const esbuild =
+ 'esbuild '
+const denoflate =
+ 'denoflate '
+const bundlejs =
+ 'bundlejs '
+
+const description = `
+View ${esbuild} minified and ${denoflate} gzipped size of a javascript package or selected exports, via ${bundlejs}.
+`
+
+export default class BundlejsPackage extends BaseJsonService {
+ static category = 'size'
+
+ static route = {
+ base: 'bundlejs/size',
+ pattern: ':scope(@[^/]+)?/:packageName+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/bundlejs/size/{packageName}': {
+ get: {
+ summary: 'npm package minimized gzipped size',
+ description,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'value-enhancer@3.1.2',
+ description:
+ 'This can either be a package name e.g: `value-enhancer`, or a package name and version e.g: `value-enhancer@3.1.2`',
+ }),
+ queryParam({
+ name: 'exports',
+ example: 'isVal,val',
+ }),
+ queryParam({
+ name: 'externals',
+ example: 'lodash,axios',
+ }),
+ queryParam({
+ name: 'format',
+ schema: { type: 'string', enum: ['min', 'minzip', 'both'] },
+ example: 'minzip',
+ }),
+ ],
+ },
+ },
+ '/bundlejs/size/{scope}/{packageName}': {
+ get: {
+ summary: 'npm package minimized gzipped size (scoped)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'scope',
+ example: '@ngneat',
+ }),
+ pathParam({
+ name: 'packageName',
+ example: 'falso@6.4.0',
+ description:
+ 'This can either be a package name e.g: `falso`, or a package name and version e.g: `falso@6.4.0`',
+ }),
+ queryParam({
+ name: 'exports',
+ example: 'randEmail,randFullName',
+ }),
+ queryParam({
+ name: 'externals',
+ example: 'lodash,axios',
+ }),
+ queryParam({
+ name: 'format',
+ schema: { type: 'string', enum: ['min', 'minzip', 'both'] },
+ example: 'minzip',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'bundlejs', color: 'informational' }
+
+ async fetch({ scope, packageName, exports, externals }) {
+ const searchParams = {
+ q: `${scope ? `${scope}/` : ''}${packageName}`,
+ }
+ if (exports) {
+ searchParams.treeshake = `[{${exports}}]`
+ }
+ if (externals) {
+ searchParams.config = JSON.stringify({
+ esbuild: { external: externals.split(',') },
+ })
+ }
+ return this._requestJson({
+ schema,
+ url: 'https://deno.bundlejs.com',
+ options: {
+ searchParams,
+ timeout: {
+ request: 3500,
+ },
+ },
+ systemErrors: {
+ ETIMEDOUT: { prettyMessage: 'timeout', cacheSeconds: 10 },
+ },
+ httpErrors: {
+ 404: 'package or version not found',
+ },
+ })
+ }
+
+ async handle({ scope, packageName }, { exports, externals, format }) {
+ const json = await this.fetch({ scope, packageName, exports, externals })
+ switch (format) {
+ case 'min':
+ return renderSizeBadge(
+ json.size.rawUncompressedSize,
+ 'metric',
+ 'minified size',
+ )
+ case 'both':
+ return {
+ label: 'size',
+ message: `${byteSize(json.size.rawUncompressedSize, { units: 'metric' })} (gzip: ${byteSize(json.size.rawCompressedSize, { units: 'metric' })})`,
+ color: 'blue',
+ }
+ default:
+ // by default use format === 'minzip'
+ // because that's how it used to be before the format query param was added
+ return renderSizeBadge(
+ json.size.rawCompressedSize,
+ 'metric',
+ 'minified size (gzip)',
+ )
+ }
+ }
+}
diff --git a/services/bundlejs/bundlejs-package.tester.js b/services/bundlejs/bundlejs-package.tester.js
new file mode 100644
index 0000000000000..5dd247aa52efa
--- /dev/null
+++ b/services/bundlejs/bundlejs-package.tester.js
@@ -0,0 +1,62 @@
+import { isMetricFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('bundlejs/package (packageName)')
+ .get('/jquery.json')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (version)')
+ .get('/react@18.2.0.json')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (scoped)')
+ .get('/@cycle/rx-run.json')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (scoped with subpath export)')
+ .get('/@noble/hashes/sha3.js.json')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (select exports)')
+ .get('/value-enhancer.json?exports=isVal,val')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (scoped version select exports)')
+ .get('/@floating-ui/dom@1.6.0.json?exports=computePosition,autoUpdate')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (externals)')
+ .get('/value-enhancer.json?externals=lodash,axios,jquery')
+ .expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
+
+t.create('bundlejs/package (format min)')
+ .get('/value-enhancer.json?format=min')
+ .expectBadge({ label: 'minified size', message: isMetricFileSize })
+
+t.create('bundlejs/package (format both)')
+ .get('/value-enhancer.json?format=both')
+ .expectBadge({
+ label: 'size',
+ message: /^[\d.]+ [kMG]?B \(gzip: [\d.]+ [kMG]?B\)$/,
+ })
+
+t.create('bundlejs/package (not found)')
+ .get('/react@18.2.0.json')
+ .intercept(nock =>
+ nock('https://deno.bundlejs.com')
+ .get(/./)
+ .query({ q: 'react@18.2.0' })
+ .reply(404),
+ )
+ .expectBadge({ label: 'bundlejs', message: 'package or version not found' })
+
+t.create('bundlejs/package (timeout)')
+ .get('/react@18.2.0.json')
+ .intercept(nock =>
+ nock('https://deno.bundlejs.com')
+ .get(/./)
+ .query({ q: 'react@18.2.0' })
+ .replyWithError({ code: 'ETIMEDOUT' }),
+ )
+ .expectBadge({ label: 'bundlejs', message: 'timeout' })
diff --git a/services/bundlephobia/bundlephobia.service.js b/services/bundlephobia/bundlephobia.service.js
index 1667da66e5a0f..437a37789f165 100644
--- a/services/bundlephobia/bundlephobia.service.js
+++ b/services/bundlephobia/bundlephobia.service.js
@@ -1,103 +1,130 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderSizeBadge } from '../size.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
size: nonNegativeInteger,
gzip: nonNegativeInteger,
}).required()
-const keywords = ['node', 'bundlephobia']
+const description =
+ '[Bundlephobia](https://bundlephobia.com) lets you understand the size of a javascript package from NPM before it becomes a part of your bundle.'
-module.exports = class Bundlephobia extends BaseJsonService {
- static get category() {
- return 'size'
- }
+export default class Bundlephobia extends BaseJsonService {
+ static category = 'size'
- static get route() {
- return {
- base: 'bundlephobia',
- pattern: ':format(min|minzip)/:scope(@[^/]+)?/:packageName/:version?',
- }
+ static route = {
+ base: 'bundlephobia',
+ pattern: ':format(min|minzip)/:scope(@[^/]+)?/:packageName/:version?',
}
- static get examples() {
- return [
- {
- title: 'npm bundle size',
- pattern: ':format(min|minzip)/:packageName',
- namedParams: {
- format: 'min',
- packageName: 'react',
- },
- staticPreview: this.render({ format: 'min', size: 6652 }),
- keywords,
+ static openApi = {
+ '/bundlephobia/{format}/{packageName}': {
+ get: {
+ summary: 'npm bundle size',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ example: 'min',
+ },
+ {
+ name: 'packageName',
+ example: 'react',
+ },
+ ),
},
- {
- title: 'npm bundle size (scoped)',
- pattern: ':format(min|minzip)/:scope/:packageName',
- namedParams: {
- format: 'min',
- scope: '@cycle',
- packageName: 'core',
- },
- staticPreview: this.render({ format: 'min', size: 3562 }),
- keywords,
+ },
+ '/bundlephobia/{format}/{scope}/{packageName}': {
+ get: {
+ summary: 'npm bundle size (scoped)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ example: 'min',
+ },
+ {
+ name: 'scope',
+ example: '@cycle',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ ),
},
- {
- title: 'npm bundle size (version)',
- pattern: ':format(min|minzip)/:packageName/:version',
- namedParams: {
- format: 'min',
- packageName: 'react',
- version: '15.0.0',
- },
- staticPreview: this.render({ format: 'min', size: 20535 }),
- keywords,
+ },
+ '/bundlephobia/{format}/{packageName}/{version}': {
+ get: {
+ summary: 'npm bundle size (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ example: 'min',
+ },
+ {
+ name: 'packageName',
+ example: 'react',
+ },
+ {
+ name: 'version',
+ example: '15.0.0',
+ },
+ ),
},
- {
- title: 'npm bundle size (scoped version)',
- pattern: ':format(min|minzip)/:scope/:packageName/:version',
- namedParams: {
- format: 'min',
- scope: '@cycle',
- packageName: 'core',
- version: '7.0.0',
- },
- staticPreview: this.render({ format: 'min', size: 3562 }),
- keywords,
+ },
+ '/bundlephobia/{format}/{scope}/{packageName}/{version}': {
+ get: {
+ summary: 'npm bundle size (scoped version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ example: 'min',
+ },
+ {
+ name: 'scope',
+ example: '@cycle',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ {
+ name: 'version',
+ example: '7.0.0',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'bundlephobia',
- color: 'informational',
- }
- }
+ static _cacheLength = 1800
+
+ static defaultBadgeData = { label: 'bundlephobia', color: 'informational' }
static render({ format, size }) {
const label = format === 'min' ? 'minified size' : 'minzipped size'
- return {
- label,
- message: prettyBytes(size),
- }
+ return renderSizeBadge(size, 'iec', label)
}
async fetch({ scope, packageName, version }) {
const packageQuery = `${scope ? `${scope}/` : ''}${packageName}${
version ? `@${version}` : ''
}`
- const options = { qs: { package: packageQuery } }
+ const options = { searchParams: { package: packageQuery } }
return this._requestJson({
schema,
url: 'https://bundlephobia.com/api/size',
options,
- errorMessages: {
+ httpErrors: {
404: 'package or version not found',
},
})
diff --git a/services/bundlephobia/bundlephobia.tester.js b/services/bundlephobia/bundlephobia.tester.js
index a4ec4d6f29ff9..3bd229094b0be 100644
--- a/services/bundlephobia/bundlephobia.tester.js
+++ b/services/bundlephobia/bundlephobia.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIecFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const formats = {
A: '/bundlephobia/:type/:package.:format',
@@ -14,42 +13,42 @@ const data = [
{
format: formats.A,
get: '/min/preact.json',
- expect: { label: 'minified size', message: isFileSize },
+ expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.B,
get: '/min/preact/8.0.0.json',
- expect: { label: 'minified size', message: isFileSize },
+ expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.C,
get: '/min/@cycle/core.json',
- expect: { label: 'minified size', message: isFileSize },
+ expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.D,
get: '/min/@cycle/core/7.0.0.json',
- expect: { label: 'minified size', message: isFileSize },
+ expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.A,
get: '/minzip/preact.json',
- expect: { label: 'minzipped size', message: isFileSize },
+ expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.B,
get: '/minzip/preact/8.0.0.json',
- expect: { label: 'minzipped size', message: isFileSize },
+ expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.C,
get: '/minzip/@cycle/core.json',
- expect: { label: 'minzipped size', message: isFileSize },
+ expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.D,
get: '/minzip/@cycle/core/7.0.0.json',
- expect: { label: 'minzipped size', message: isFileSize },
+ expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.A,
diff --git a/services/cangjie/cangjie-version.service.js b/services/cangjie/cangjie-version.service.js
new file mode 100644
index 0000000000000..1da5dde6cb348
--- /dev/null
+++ b/services/cangjie/cangjie-version.service.js
@@ -0,0 +1,104 @@
+import Joi from 'joi'
+import {
+ BaseJsonlService,
+ InvalidParameter,
+ NotFound,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { latest, renderVersionBadge } from '../version.js'
+
+const queryParamSchema = Joi.object({
+ organization: Joi.string(),
+}).required()
+
+const indexEntrySchema = Joi.object({
+ organization: Joi.string(),
+ name: Joi.string().required(),
+ version: Joi.string().required(),
+ yanked: Joi.boolean().required(),
+ 'index-version': Joi.string().required(),
+}).required()
+
+const description =
+ '[Cangjie Central Repository](https://pkg.cangjie-lang.cn/index) hosts packages for the Cangjie programming language.'
+
+export default class CangjieVersion extends BaseJsonlService {
+ static category = 'version'
+ static route = {
+ base: 'cangjie',
+ pattern: ':moduleName',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/cangjie/{moduleName}': {
+ get: {
+ summary: 'Cangjie Version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'moduleName',
+ example: 'stdx',
+ }),
+ queryParam({
+ name: 'organization',
+ example: 'fountain',
+ description: 'Optional organization name for organization modules.',
+ required: false,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'cangjie' }
+
+ static getIndexPath({ moduleName }) {
+ if (moduleName.length < 3) {
+ throw new InvalidParameter({ prettyMessage: 'invalid module name' })
+ }
+
+ if (moduleName.length === 3) {
+ return `${moduleName.slice(0, 2)}/${moduleName[2]}/${moduleName}`
+ }
+
+ return `${moduleName.slice(0, 2)}/${moduleName.slice(2, 4)}/${moduleName}`
+ }
+
+ static getLatestVersion(entries) {
+ const version = latest(
+ entries.filter(({ yanked }) => !yanked).map(({ version }) => version),
+ )
+
+ if (version === undefined) {
+ throw new NotFound({ prettyMessage: 'no releases' })
+ }
+
+ return version
+ }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async fetch({ moduleName, organization }) {
+ const indexPath = this.constructor.getIndexPath({ moduleName })
+ const options = organization
+ ? { searchParams: { organization } }
+ : undefined
+
+ return this._requestJsonl({
+ schema: indexEntrySchema,
+ url: `https://pkg.cangjie-lang.cn/registry/index/${indexPath}`,
+ options,
+ prettyErrorMessage: 'invalid index entry',
+ })
+ }
+
+ async handle({ moduleName }, { organization }) {
+ const entries = await this.fetch({ moduleName, organization })
+ const version = this.constructor.getLatestVersion(entries)
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/cangjie/cangjie-version.spec.js b/services/cangjie/cangjie-version.spec.js
new file mode 100644
index 0000000000000..d26291eb7acf8
--- /dev/null
+++ b/services/cangjie/cangjie-version.spec.js
@@ -0,0 +1,109 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import sinon from 'sinon'
+import { InvalidResponse } from '../index.js'
+import CangjieVersion from './cangjie-version.service.js'
+
+describe('CangjieVersion', function () {
+ describe('getIndexPath', function () {
+ test(CangjieVersion.getIndexPath, () => {
+ given({ moduleName: 'dep' }).expect('de/p/dep')
+ given({ moduleName: 'aabbcc' }).expect('aa/bb/aabbcc')
+ given({ moduleName: 'f_store' }).expect('f_/st/f_store')
+ })
+
+ it('rejects undocumented short module names', function () {
+ expect(() => CangjieVersion.getIndexPath({ moduleName: 'ab' })).to.throw()
+ })
+ })
+
+ describe('fetch', function () {
+ it('parses line-delimited index entries', async function () {
+ const requestFetcher = sinon.stub().resolves({
+ buffer: `
+{"name":"demo","version":"1.0.0","yanked":false,"index-version":"1"}
+{"name":"demo","version":"1.1.0","yanked":true,"index-version":"1"}
+ `,
+ res: { statusCode: 200 },
+ })
+
+ const service = new CangjieVersion(
+ { requestFetcher },
+ { handleInternalErrors: false },
+ )
+
+ expect(await service.fetch({ moduleName: 'demo' })).to.deep.equal([
+ {
+ name: 'demo',
+ version: '1.0.0',
+ yanked: false,
+ 'index-version': '1',
+ },
+ {
+ name: 'demo',
+ version: '1.1.0',
+ yanked: true,
+ 'index-version': '1',
+ },
+ ])
+ })
+
+ it('throws unparseable jsonl response for malformed jsonl', async function () {
+ const requestFetcher = sinon.stub().resolves({
+ buffer: 'not json',
+ res: { statusCode: 200 },
+ })
+
+ const service = new CangjieVersion(
+ { requestFetcher },
+ { handleInternalErrors: false },
+ )
+
+ try {
+ await service.fetch({ moduleName: 'demo' })
+ expect.fail('expected fetch() to throw')
+ } catch (e) {
+ expect(e).to.be.instanceOf(InvalidResponse)
+ expect(e.prettyMessage).to.equal('unparseable jsonl response')
+ }
+ })
+
+ it('throws invalid index entry for schema-invalid lines', async function () {
+ const requestFetcher = sinon.stub().resolves({
+ buffer: '{"name":"demo","version":"1.0.0","index-version":"1"}',
+ res: { statusCode: 200 },
+ })
+
+ const service = new CangjieVersion(
+ { requestFetcher },
+ { handleInternalErrors: false },
+ )
+
+ try {
+ await service.fetch({ moduleName: 'demo' })
+ expect.fail('expected fetch() to throw')
+ } catch (e) {
+ expect(e).to.be.instanceOf(InvalidResponse)
+ expect(e.prettyMessage).to.equal('invalid index entry')
+ }
+ })
+ })
+
+ describe('getLatestVersion', function () {
+ it('selects the highest non-yanked version', function () {
+ const entries = [
+ { version: '1.0.0', yanked: false },
+ { version: '1.1.0', yanked: true },
+ { version: '1.0.5', yanked: false },
+ ]
+
+ expect(CangjieVersion.getLatestVersion(entries)).to.equal('1.0.5')
+ })
+
+ it('throws when all versions are yanked', function () {
+ expect(() =>
+ CangjieVersion.getLatestVersion([{ version: '1.0.0', yanked: true }]),
+ ).to.throw()
+ })
+ })
+})
diff --git a/services/cangjie/cangjie-version.tester.js b/services/cangjie/cangjie-version.tester.js
new file mode 100644
index 0000000000000..5e8d7801c53aa
--- /dev/null
+++ b/services/cangjie/cangjie-version.tester.js
@@ -0,0 +1,92 @@
+import { isVPlusDottedVersionNClausesWithOptionalSuffix as isVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('live cangjie version unicode_case')
+ .get('/unicode_case.json')
+ .expectBadge({
+ label: 'cangjie',
+ message: isVersion,
+ })
+
+t.create('live cangjie version organization')
+ .get('/f_store.json?organization=fountain')
+ .expectBadge({
+ label: 'cangjie',
+ message: isVersion,
+ })
+
+t.create('version')
+ .get('/stdx.json')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/st/dx/stdx')
+ .reply(
+ 200,
+ [
+ '{"name":"stdx","version":"0.0.1","yanked":false,"index-version":"1"}',
+ '{"name":"stdx","version":"0.0.3","yanked":false,"index-version":"1"}',
+ '{"name":"stdx","version":"0.0.2","yanked":false,"index-version":"1"}',
+ ].join('\n'),
+ ),
+ )
+ .expectBadge({ label: 'cangjie', message: 'v0.0.3' })
+
+t.create('version (organization)')
+ .get('/f_store.json?organization=fountain')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/f_/st/f_store')
+ .query({ organization: 'fountain' })
+ .reply(
+ 200,
+ '{"organization":"fountain","name":"f_store","version":"1.1.2","yanked":false,"index-version":"1"}',
+ ),
+ )
+ .expectBadge({ label: 'cangjie', message: 'v1.1.2' })
+
+t.create('version ignores yanked releases')
+ .get('/demo.json')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/de/mo/demo')
+ .reply(
+ 200,
+ [
+ '{"name":"demo","version":"1.0.0","yanked":false,"index-version":"1"}',
+ '{"name":"demo","version":"1.1.0","yanked":true,"index-version":"1"}',
+ ].join('\n'),
+ ),
+ )
+ .expectBadge({ label: 'cangjie', message: 'v1.0.0' })
+
+t.create('version (invalid module name)')
+ .get('/ab.json')
+ .expectBadge({ label: 'cangjie', message: 'invalid module name' })
+
+t.create('version (unparseable jsonl response)')
+ .get('/stdx.json')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/st/dx/stdx')
+ .reply(200, 'not json'),
+ )
+ .expectBadge({ label: 'cangjie', message: 'unparseable jsonl response' })
+
+t.create('version (invalid index entry)')
+ .get('/stdx.json')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/st/dx/stdx')
+ .reply(200, '{"name":"stdx","version":"0.0.1","index-version":"1"}'),
+ )
+ .expectBadge({ label: 'cangjie', message: 'invalid index entry' })
+
+t.create('version (not found)')
+ .get('/not-a-real-package.json')
+ .intercept(nock =>
+ nock('https://pkg.cangjie-lang.cn')
+ .get('/registry/index/no/t-/not-a-real-package')
+ .reply(404),
+ )
+ .expectBadge({ label: 'cangjie', message: 'not found' })
diff --git a/services/categories.js b/services/categories.js
index fc45a33520bd7..72737b529d5d6 100644
--- a/services/categories.js
+++ b/services/categories.js
@@ -1,25 +1,23 @@
-'use strict'
-
-module.exports = [
- { id: 'build', name: 'Build', keywords: ['build'] },
- { id: 'coverage', name: 'Code Coverage', keywords: ['coverage'] },
- { id: 'analysis', name: 'Analysis', keywords: ['analysis'] },
- { id: 'chat', name: 'Chat', keywords: ['chat'] },
- { id: 'dependencies', name: 'Dependencies', keywords: ['dependencies'] },
- { id: 'size', name: 'Size', keywords: ['size'] },
- { id: 'downloads', name: 'Downloads', keywords: ['downloads'] },
- { id: 'funding', name: 'Funding', keywords: ['funding'] },
- { id: 'issue-tracking', name: 'Issue Tracking', keywords: ['issue'] },
- { id: 'license', name: 'License', keywords: ['license'] },
- { id: 'rating', name: 'Rating', keywords: ['rating'] },
- { id: 'social', name: 'Social', keywords: ['social'] },
- { id: 'version', name: 'Version', keywords: ['version'] },
+export default [
+ { id: 'build', name: 'Build' },
+ { id: 'coverage', name: 'Code Coverage' },
+ { id: 'test-results', name: 'Test Results' },
+ { id: 'analysis', name: 'Analysis' },
+ { id: 'chat', name: 'Chat' },
+ { id: 'dependencies', name: 'Dependencies' },
+ { id: 'size', name: 'Size' },
+ { id: 'downloads', name: 'Downloads' },
+ { id: 'funding', name: 'Funding' },
+ { id: 'issue-tracking', name: 'Issue Tracking' },
+ { id: 'license', name: 'License' },
+ { id: 'rating', name: 'Rating' },
+ { id: 'social', name: 'Social' },
+ { id: 'version', name: 'Version' },
{
id: 'platform-support',
name: 'Platform & Version Support',
- keywords: ['platform'],
},
- { id: 'monitoring', name: 'Monitoring', keywords: ['monitoring'] },
- { id: 'activity', name: 'Activity', keywords: ['activity'] },
- { id: 'other', name: 'Other', keywords: [] },
+ { id: 'monitoring', name: 'Monitoring' },
+ { id: 'activity', name: 'Activity' },
+ { id: 'other', name: 'Other' },
]
diff --git a/services/cauditor/cauditor.service.js b/services/cauditor/cauditor.service.js
deleted file mode 100644
index 64cfbf49dd2f3..0000000000000
--- a/services/cauditor/cauditor.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'other',
- route: {
- base: 'cauditor',
- pattern: ':various*',
- },
- label: 'cauditor',
- dateAdded: new Date('2018-02-15'),
-})
diff --git a/services/cauditor/cauditor.tester.js b/services/cauditor/cauditor.tester.js
deleted file mode 100644
index aa23655b92df4..0000000000000
--- a/services/cauditor/cauditor.tester.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'cauditor',
- title: 'Cauditor',
-}))
-
-t.create('no longer available')
- .get('/mi/matthiasmullie/scrapbook/master.json')
- .expectBadge({
- label: 'cauditor',
- message: 'no longer available',
- })
diff --git a/services/cdnjs/cdnjs.service.js b/services/cdnjs/cdnjs.service.js
index a9b600d710c2a..fff7ec39fa5d8 100644
--- a/services/cdnjs/cdnjs.service.js
+++ b/services/cdnjs/cdnjs.service.js
@@ -1,38 +1,29 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
const cdnjsSchema = Joi.object({
// optional due to non-standard 'not found' condition
version: Joi.string(),
}).required()
-module.exports = class Cdnjs extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'cdnjs/v',
- pattern: ':library',
- }
- }
-
- static get examples() {
- return [
- {
- namedParams: { library: 'jquery' },
- staticPreview: this.render({ version: '1.5.2' }),
+export default class Cdnjs extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'cdnjs/v', pattern: ':library' }
+
+ static openApi = {
+ '/cdnjs/v/{library}': {
+ get: {
+ summary: 'Cdnjs',
+ parameters: pathParams({
+ name: 'library',
+ example: 'jquery',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'cdnjs' }
- }
+ static defaultBadgeData = { label: 'cdnjs' }
static render({ version }) {
return renderVersionBadge({ version })
diff --git a/services/cdnjs/cdnjs.tester.js b/services/cdnjs/cdnjs.tester.js
index 394384d065e8d..a2da7ec37576c 100644
--- a/services/cdnjs/cdnjs.tester.js
+++ b/services/cdnjs/cdnjs.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('cdnjs (valid)')
- .get('/jquery.json')
- .expectBadge({
- label: 'cdnjs',
- message: isVPlusTripleDottedVersion,
- })
+t.create('cdnjs (valid)').get('/jquery.json').expectBadge({
+ label: 'cdnjs',
+ message: isVPlusTripleDottedVersion,
+})
t.create('cdnjs (not found)')
.get('/not-a-library.json')
diff --git a/services/check-services.spec.js b/services/check-services.spec.js
index 605ad0539f3a7..3f5e99c20a6bf 100644
--- a/services/check-services.spec.js
+++ b/services/check-services.spec.js
@@ -1,17 +1,16 @@
-'use strict'
-
-const {
- checkNames,
+import {
+ loadServiceClasses,
collectDefinitions,
-} = require('../core/base-service/loader')
+} from '../core/base-service/loader.js'
// When these tests fail, they will throw AssertionErrors. Wrapping them in an
// `expect().not.to.throw()` makes the error output unreadable.
-it('Services have unique names', function() {
- checkNames()
+it('Services have unique names', async function () {
+ this.timeout(30000)
+ await loadServiceClasses()
})
-it('Can collect the service definitions', function() {
- collectDefinitions()
+it('Can collect the service definitions', async function () {
+ await collectDefinitions()
})
diff --git a/services/chocolatey/chocolatey.service.js b/services/chocolatey/chocolatey.service.js
index c03f3d1a7cda8..16cb8daa457df 100644
--- a/services/chocolatey/chocolatey.service.js
+++ b/services/chocolatey/chocolatey.service.js
@@ -1,15 +1,9 @@
-'use strict'
+import { createServiceFamily } from '../nuget/nuget-v2-service-family.js'
-const { createServiceFamily } = require('../nuget/nuget-v2-service-family')
-
-module.exports = createServiceFamily({
+export default createServiceFamily({
defaultLabel: 'chocolatey',
serviceBaseUrl: 'chocolatey',
- apiBaseUrl: 'https://www.chocolatey.org/api/v2',
- odataFormat: 'json',
+ apiBaseUrl: 'https://community.chocolatey.org/api/v2',
title: 'Chocolatey',
examplePackageName: 'git',
- exampleVersion: '2.19.2',
- examplePrereleaseVersion: '2.19.2',
- exampleDownloadCount: 2.2e6,
})
diff --git a/services/chocolatey/chocolatey.tester.js b/services/chocolatey/chocolatey.tester.js
index 45eb272dfa41a..c2efd149c5de7 100644
--- a/services/chocolatey/chocolatey.tester.js
+++ b/services/chocolatey/chocolatey.tester.js
@@ -1,25 +1,21 @@
-'use strict'
-
-const {
+import {
isMetric,
isVPlusDottedVersionNClauses,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const { ServiceTester } = require('../tester')
+} from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'chocolatey',
title: 'Chocolatey',
-}))
+})
// downloads
-t.create('total downloads (valid)')
- .get('/dt/scriptcs.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('total downloads (valid)').get('/dt/scriptcs.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('total downloads (not found)')
.get('/dt/not-a-real-package.json')
@@ -27,12 +23,10 @@ t.create('total downloads (not found)')
// version
-t.create('version (valid)')
- .get('/v/scriptcs.json')
- .expectBadge({
- label: 'chocolatey',
- message: isVPlusDottedVersionNClauses,
- })
+t.create('version (valid)').get('/v/scriptcs.json').expectBadge({
+ label: 'chocolatey',
+ message: isVPlusDottedVersionNClauses,
+})
t.create('version (not found)')
.get('/v/not-a-real-package.json')
@@ -52,5 +46,8 @@ t.create('version (pre) (not found)')
.expectBadge({ label: 'chocolatey', message: 'not found' })
t.create('version (legacy redirect: vpre)')
- .get('/vpre/scriptcs.svg')
- .expectRedirect('/chocolatey/v/scriptcs.svg?include_prereleases')
+ .get('/vpre/scriptcs.json')
+ .expectBadge({
+ label: 'chocolatey',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/chrome-web-store/chrome-web-store-base.js b/services/chrome-web-store/chrome-web-store-base.js
index cbae564c2df1f..ecc1eb71bc258 100644
--- a/services/chrome-web-store/chrome-web-store-base.js
+++ b/services/chrome-web-store/chrome-web-store-base.js
@@ -1,22 +1,23 @@
-'use strict'
+import { ChromeWebStore } from 'webextension-store-meta/lib/chrome-web-store/index.js'
+import checkErrorResponse from '../../core/base-service/check-error-response.js'
+import { BaseService, Inaccessible } from '../index.js'
-const chromeWebStore = require('chrome-web-store-item-property')
-const checkErrorResponse = require('../../core/base-service/check-error-response')
-const { BaseService, Inaccessible } = require('..')
+const description = `
+Our servers make requests to the Chrome Web Store from the US East region. Therefore, the extension must not be regionally restricted by the publisher for that location.
+`
-module.exports = class BaseChromeWebStoreService extends BaseService {
+export { description }
+
+export default class BaseChromeWebStoreService extends BaseService {
async fetch({ storeId }) {
try {
- return await chromeWebStore(storeId)
+ return await ChromeWebStore.load({ id: storeId })
} catch (e) {
- if (e.statusCode === undefined) {
+ const statusCode = parseInt(e.message)
+ if (isNaN(statusCode)) {
throw new Inaccessible({ underlyingError: e })
}
- /*
- chrome-web-store-item-property's `HTTPError` object has a
- `statusCode` property so we can pass `e` to `checkErrorResponse`
- to throw the correct `ShieldsRuntimeError` for us.
- */
+ e.statusCode = statusCode
return checkErrorResponse({})({ buffer: '', res: e })
}
}
diff --git a/services/chrome-web-store/chrome-web-store-last-updated.service.js b/services/chrome-web-store/chrome-web-store-last-updated.service.js
new file mode 100644
index 0000000000000..a2872ac4dc17e
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-last-updated.service.js
@@ -0,0 +1,38 @@
+import { renderDateBadge } from '../date.js'
+import { NotFound, pathParams } from '../index.js'
+import BaseChromeWebStoreService, {
+ description,
+} from './chrome-web-store-base.js'
+
+export default class ChromeWebStoreLastUpdated extends BaseChromeWebStoreService {
+ static category = 'activity'
+ static route = { base: 'chrome-web-store/last-updated', pattern: ':storeId' }
+
+ static openApi = {
+ '/chrome-web-store/last-updated/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Last Updated',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'nccfelhkfpbnefflolffkclhenplhiab',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'last updated',
+ }
+
+ async handle({ storeId }) {
+ const chromeWebStore = await this.fetch({ storeId })
+ const lastUpdated = chromeWebStore.lastUpdated()
+
+ if (lastUpdated == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
+
+ return renderDateBadge(lastUpdated)
+ }
+}
diff --git a/services/chrome-web-store/chrome-web-store-last-updated.tester.js b/services/chrome-web-store/chrome-web-store-last-updated.tester.js
new file mode 100644
index 0000000000000..3459b3702c28f
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-last-updated.tester.js
@@ -0,0 +1,18 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Last updated')
+ .get('/nccfelhkfpbnefflolffkclhenplhiab.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('Last updated (not found)')
+ .get('/invalid-name-of-addon.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'not found',
+ })
diff --git a/services/chrome-web-store/chrome-web-store-price.service.js b/services/chrome-web-store/chrome-web-store-price.service.js
deleted file mode 100644
index 9bf92bb35dd05..0000000000000
--- a/services/chrome-web-store/chrome-web-store-price.service.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict'
-
-const { currencyFromCode } = require('../text-formatters')
-const BaseChromeWebStoreService = require('./chrome-web-store-base')
-
-module.exports = class ChromeWebStorePrice extends BaseChromeWebStoreService {
- static get category() {
- return 'funding'
- }
-
- static get route() {
- return {
- base: 'chrome-web-store/price',
- pattern: ':storeId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: this.render({ priceCurrency: 'USD', price: 0 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'price' }
- }
-
- static render({ priceCurrency, price }) {
- return {
- message: `${currencyFromCode(priceCurrency) + price}`,
- color: 'brightgreen',
- }
- }
-
- async handle({ storeId }) {
- const { priceCurrency, price } = await this.fetch({ storeId })
- return this.constructor.render({ priceCurrency, price })
- }
-}
diff --git a/services/chrome-web-store/chrome-web-store-price.tester.js b/services/chrome-web-store/chrome-web-store-price.tester.js
deleted file mode 100644
index 4f0201aa71e35..0000000000000
--- a/services/chrome-web-store/chrome-web-store-price.tester.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Price')
- .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .expectBadge({
- label: 'price',
- message: Joi.string().regex(/^\$\d+(.\d{1,2})?$/),
- })
-
-t.create('Price (not found)')
- .get('/invalid-name-of-addon.json')
- .expectBadge({ label: 'price', message: 'not found' })
-
-// Keep this "inaccessible" test, since this service does not use BaseService#_request.
-t.create('Price (inaccessible)')
- .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .networkOff()
- .expectBadge({ label: 'price', message: 'inaccessible' })
diff --git a/services/chrome-web-store/chrome-web-store-rating.service.js b/services/chrome-web-store/chrome-web-store-rating.service.js
index bc3855c78f719..84fff759e0772 100644
--- a/services/chrome-web-store/chrome-web-store-rating.service.js
+++ b/services/chrome-web-store/chrome-web-store-rating.service.js
@@ -1,35 +1,33 @@
-'use strict'
-
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { metric, starRating } = require('../text-formatters')
-const BaseChromeWebStoreService = require('./chrome-web-store-base')
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { metric, starRating } from '../text-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import BaseChromeWebStoreService, {
+ description,
+} from './chrome-web-store-base.js'
class BaseChromeWebStoreRating extends BaseChromeWebStoreService {
- static get category() {
- return 'rating'
- }
+ static category = 'rating'
- static get defaultBadgeData() {
- return { label: 'rating' }
- }
+ static defaultBadgeData = { label: 'rating' }
}
class ChromeWebStoreRating extends BaseChromeWebStoreRating {
- static get route() {
- return {
- base: 'chrome-web-store/rating',
- pattern: ':storeId',
- }
+ static route = {
+ base: 'chrome-web-store/rating',
+ pattern: ':storeId',
}
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: this.render({ rating: '3.67' }),
+ static openApi = {
+ '/chrome-web-store/rating/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Rating',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'ogffaloegjglncjfehdfplabnoondfjo',
+ }),
},
- ]
+ },
}
static render({ rating }) {
@@ -41,27 +39,32 @@ class ChromeWebStoreRating extends BaseChromeWebStoreRating {
}
async handle({ storeId }) {
- const { ratingValue } = await this.fetch({ storeId })
- return this.constructor.render({ rating: ratingValue })
+ const chromeWebStore = await this.fetch({ storeId })
+ const rating = chromeWebStore.ratingValue()
+ if (rating == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
+ return this.constructor.render({ rating })
}
}
class ChromeWebStoreRatingCount extends BaseChromeWebStoreRating {
- static get route() {
- return {
- base: 'chrome-web-store/rating-count',
- pattern: ':storeId',
- }
+ static route = {
+ base: 'chrome-web-store/rating-count',
+ pattern: ':storeId',
}
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: this.render({ ratingCount: 12 }),
+ static openApi = {
+ '/chrome-web-store/rating-count/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Rating Count',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'ogffaloegjglncjfehdfplabnoondfjo',
+ }),
},
- ]
+ },
}
static render({ ratingCount }) {
@@ -72,27 +75,35 @@ class ChromeWebStoreRatingCount extends BaseChromeWebStoreRating {
}
async handle({ storeId }) {
- const { ratingCount } = await this.fetch({ storeId })
+ const chromeWebStore = await this.fetch({
+ storeId,
+ property: 'ratingCount',
+ })
+ const ratingCount = chromeWebStore.ratingCount()
+ if (ratingCount == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
return this.constructor.render({ ratingCount })
}
}
class ChromeWebStoreRatingStars extends BaseChromeWebStoreRating {
- static get route() {
- return {
- base: 'chrome-web-store/stars',
- pattern: ':storeId',
- }
+ static route = {
+ base: 'chrome-web-store/stars',
+ pattern: ':storeId',
}
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: this.render({ rating: '3.75' }),
+ static openApi = {
+ '/chrome-web-store/stars/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Stars',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'ogffaloegjglncjfehdfplabnoondfjo',
+ }),
},
- ]
+ },
}
static render({ rating }) {
@@ -103,12 +114,16 @@ class ChromeWebStoreRatingStars extends BaseChromeWebStoreRating {
}
async handle({ storeId }) {
- const { ratingValue } = await this.fetch({ storeId })
- return this.constructor.render({ rating: parseFloat(ratingValue) })
+ const chromeWebStore = await this.fetch({ storeId })
+ const rating = chromeWebStore.ratingValue()
+ if (rating == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
+ return this.constructor.render({ rating })
}
}
-module.exports = {
+export {
ChromeWebStoreRating,
ChromeWebStoreRatingCount,
ChromeWebStoreRatingStars,
diff --git a/services/chrome-web-store/chrome-web-store-rating.tester.js b/services/chrome-web-store/chrome-web-store-rating.tester.js
index a364b0fde151a..27948c53d9c0a 100644
--- a/services/chrome-web-store/chrome-web-store-rating.tester.js
+++ b/services/chrome-web-store/chrome-web-store-rating.tester.js
@@ -1,14 +1,13 @@
-'use strict'
+import Joi from 'joi'
+import { Agent, MockAgent, setGlobalDispatcher } from 'undici'
+import { isStarRating } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { isStarRating } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'ChromeWebStoreRating',
title: 'Chrome Web Store Rating',
pathPrefix: '/chrome-web-store',
-}))
+})
t.create('Rating')
.get('/rating/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
@@ -41,7 +40,16 @@ t.create('Stars (not found)')
.expectBadge({ label: 'rating', message: 'not found' })
// Keep this "inaccessible" test, since this service does not use BaseService#_request.
+const mockAgent = new MockAgent()
t.create('Rating (inaccessible)')
.get('/rating/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .networkOff()
+ // webextension-store-meta uses undici internally, so we can't mock it with nock
+ .before(function () {
+ setGlobalDispatcher(mockAgent)
+ mockAgent.disableNetConnect()
+ })
+ .after(async function () {
+ await mockAgent.close()
+ setGlobalDispatcher(new Agent())
+ })
.expectBadge({ label: 'rating', message: 'inaccessible' })
diff --git a/services/chrome-web-store/chrome-web-store-size.service.js b/services/chrome-web-store/chrome-web-store-size.service.js
new file mode 100644
index 0000000000000..ee2425aadd0dd
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-size.service.js
@@ -0,0 +1,49 @@
+import { InvalidResponse, NotFound, pathParams } from '../index.js'
+import BaseChromeWebStoreService, {
+ description,
+} from './chrome-web-store-base.js'
+
+export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
+ static category = 'size'
+ static route = { base: 'chrome-web-store/size', pattern: ':storeId' }
+
+ static openApi = {
+ '/chrome-web-store/size/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Size',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'nccfelhkfpbnefflolffkclhenplhiab',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'extension size',
+ color: 'blue',
+ }
+
+ static transform(sizeStr) {
+ const match = sizeStr.match(/^(\d+(?:\.\d+)?)([a-zA-Z]+)$/)
+ if (!match) {
+ throw new InvalidResponse({
+ prettyMessage: 'size does not match expected format',
+ })
+ }
+ const [, size, units] = match
+ return `${size} ${units}`
+ }
+
+ async handle({ storeId }) {
+ const chromeWebStore = await this.fetch({ storeId })
+ const size = chromeWebStore.size()
+
+ if (size == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
+
+ return { message: this.constructor.transform(size) }
+ }
+}
diff --git a/services/chrome-web-store/chrome-web-store-size.spec.js b/services/chrome-web-store/chrome-web-store-size.spec.js
new file mode 100644
index 0000000000000..e7d940ca75732
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-size.spec.js
@@ -0,0 +1,28 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import ChromeWebStoreSize from './chrome-web-store-size.service.js'
+
+describe('transform function', function () {
+ it('formats size correctly', function () {
+ test(ChromeWebStoreSize.transform, () => {
+ given('0.55KiB').expect('0.55 KiB')
+ given('19.86KiB').expect('19.86 KiB')
+ given('432KiB').expect('432 KiB')
+ })
+ })
+
+ it('throws when the format is unexpected', function () {
+ expect(() => ChromeWebStoreSize.transform('432 KiB')).to.throw(
+ InvalidResponse,
+ )
+ expect(() => ChromeWebStoreSize.transform('432')).to.throw(InvalidResponse)
+ expect(() => ChromeWebStoreSize.transform('KiB')).to.throw(InvalidResponse)
+ expect(() => ChromeWebStoreSize.transform('foobar')).to.throw(
+ InvalidResponse,
+ )
+ expect(() => ChromeWebStoreSize.transform('4.4.4 KiB')).to.throw(
+ InvalidResponse,
+ )
+ })
+})
diff --git a/services/chrome-web-store/chrome-web-store-size.tester.js b/services/chrome-web-store/chrome-web-store-size.tester.js
new file mode 100644
index 0000000000000..40e8d62661903
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-size.tester.js
@@ -0,0 +1,13 @@
+import { createServiceTester } from '../tester.js'
+import { isIecFileSize } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Size').get('/nccfelhkfpbnefflolffkclhenplhiab.json').expectBadge({
+ label: 'extension size',
+ message: isIecFileSize,
+})
+
+t.create('Size (not found)')
+ .get('/invalid-name-of-addon.json')
+ .expectBadge({ label: 'extension size', message: 'not found' })
diff --git a/services/chrome-web-store/chrome-web-store-users.service.js b/services/chrome-web-store/chrome-web-store-users.service.js
index 8180d07294889..2f12709c8f2a8 100644
--- a/services/chrome-web-store/chrome-web-store-users.service.js
+++ b/services/chrome-web-store/chrome-web-store-users.service.js
@@ -1,50 +1,40 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { redirector, NotFound } = require('..')
-const BaseChromeWebStoreService = require('./chrome-web-store-base')
+import { renderDownloadsBadge } from '../downloads.js'
+import { redirector, NotFound, pathParams } from '../index.js'
+import BaseChromeWebStoreService, {
+ description,
+} from './chrome-web-store-base.js'
class ChromeWebStoreUsers extends BaseChromeWebStoreService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'chrome-web-store/users',
- pattern: ':storeId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: this.render({ downloads: 573 }),
+ static category = 'downloads'
+ static route = { base: 'chrome-web-store/users', pattern: ':storeId' }
+
+ static openApi = {
+ '/chrome-web-store/users/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Users',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'ogffaloegjglncjfehdfplabnoondfjo',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'users' }
- }
+ static defaultBadgeData = { label: 'users' }
- static render({ downloads }) {
- return {
- message: `${metric(downloads)}`,
- color: downloadCount(downloads),
- }
+ static transform(users) {
+ return String(users.replaceAll(',', ''))
}
async handle({ storeId }) {
- const data = await this.fetch({ storeId })
- if (!data.interactionCount || !data.interactionCount.UserDownloads) {
- throw new NotFound({ prettyMessage: 'count not found' })
+ const chromeWebStore = await this.fetch({ storeId })
+ const downloads = chromeWebStore.users()
+ if (downloads == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
}
- return this.constructor.render({
- downloads: data.interactionCount.UserDownloads,
+ return renderDownloadsBadge({
+ downloads: this.constructor.transform(downloads),
})
}
}
@@ -59,7 +49,4 @@ const ChromeWebStoreDownloads = redirector({
dateAdded: new Date('2019-02-27'),
})
-module.exports = {
- ChromeWebStoreDownloads,
- ChromeWebStoreUsers,
-}
+export { ChromeWebStoreDownloads, ChromeWebStoreUsers }
diff --git a/services/chrome-web-store/chrome-web-store-users.spec.js b/services/chrome-web-store/chrome-web-store-users.spec.js
new file mode 100644
index 0000000000000..4effb4e81151c
--- /dev/null
+++ b/services/chrome-web-store/chrome-web-store-users.spec.js
@@ -0,0 +1,15 @@
+import { test, given } from 'sazerac'
+import { ChromeWebStoreUsers } from './chrome-web-store-users.service.js'
+
+describe('ChromeWebStoreUsers', function () {
+ describe('transform', function () {
+ it('strips every thousands separator, not just the first', function () {
+ test(ChromeWebStoreUsers.transform, () => {
+ given('8,000').expect('8000')
+ given('500,000').expect('500000')
+ given('3,000,000').expect('3000000')
+ given('12,000,000').expect('12000000')
+ })
+ })
+ })
+})
diff --git a/services/chrome-web-store/chrome-web-store-users.tester.js b/services/chrome-web-store/chrome-web-store-users.tester.js
index 96e1662d9ac7e..6b02b701d5468 100644
--- a/services/chrome-web-store/chrome-web-store-users.tester.js
+++ b/services/chrome-web-store/chrome-web-store-users.tester.js
@@ -1,54 +1,38 @@
-'use strict'
+import { Agent, MockAgent, setGlobalDispatcher } from 'undici'
+import { isMetric } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
-const { isMetric } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-const pageWithoutUserDownloads = `
-
-
-
-
-
-
-
-`
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'ChromeWebStoreUsers',
title: 'Chrome Web Store Users',
pathPrefix: '/chrome-web-store',
-}))
+})
t.create('Downloads (redirect)')
.get('/d/alhjnofcnnpeaphgeakdhkebafjcpeae.svg')
.expectRedirect(
- '/chrome-web-store/users/alhjnofcnnpeaphgeakdhkebafjcpeae.svg'
+ '/chrome-web-store/users/alhjnofcnnpeaphgeakdhkebafjcpeae.svg',
)
t.create('Users')
.get('/users/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
.expectBadge({ label: 'users', message: isMetric })
-t.create('Users (count not found)')
- .get('/users/ogffaloegjglncjfehdfplabnoondfjo.json')
- .intercept(nock =>
- nock('https://chrome.google.com')
- .get('/webstore/detail/ogffaloegjglncjfehdfplabnoondfjo?hl=en&gl=US')
- .reply(200, pageWithoutUserDownloads)
- )
- .expectBadge({
- label: 'users',
- message: 'count not found',
- color: 'red',
- })
-
t.create('Users (not found)')
.get('/users/invalid-name-of-addon.json')
.expectBadge({ label: 'users', message: 'not found' })
// Keep this "inaccessible" test, since this service does not use BaseService#_request.
+const mockAgent = new MockAgent()
t.create('Users (inaccessible)')
.get('/users/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .networkOff()
+ // webextension-store-meta uses undici internally, so we can't mock it with nock
+ .before(function () {
+ setGlobalDispatcher(mockAgent)
+ mockAgent.disableNetConnect()
+ })
+ .after(async function () {
+ await mockAgent.close()
+ setGlobalDispatcher(new Agent())
+ })
.expectBadge({ label: 'users', message: 'inaccessible' })
diff --git a/services/chrome-web-store/chrome-web-store-version.service.js b/services/chrome-web-store/chrome-web-store-version.service.js
index f2c384a258e4c..e20c487daf429 100644
--- a/services/chrome-web-store/chrome-web-store-version.service.js
+++ b/services/chrome-web-store/chrome-web-store-version.service.js
@@ -1,36 +1,34 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { NotFound, pathParams } from '../index.js'
+import BaseChromeWebStoreService, {
+ description,
+} from './chrome-web-store-base.js'
-const { renderVersionBadge } = require('../version')
-const BaseChromeWebStoreService = require('./chrome-web-store-base')
+export default class ChromeWebStoreVersion extends BaseChromeWebStoreService {
+ static category = 'version'
+ static route = { base: 'chrome-web-store/v', pattern: ':storeId' }
-module.exports = class ChromeWebStoreVersion extends BaseChromeWebStoreService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'chrome-web-store/v',
- pattern: ':storeId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Chrome Web Store',
- namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
- staticPreview: renderVersionBadge({ version: 'v1.1.0' }),
+ static openApi = {
+ '/chrome-web-store/v/{storeId}': {
+ get: {
+ summary: 'Chrome Web Store Version',
+ description,
+ parameters: pathParams({
+ name: 'storeId',
+ example: 'ogffaloegjglncjfehdfplabnoondfjo',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'chrome web store' }
- }
+ static defaultBadgeData = { label: 'chrome web store' }
async handle({ storeId }) {
- const data = await this.fetch({ storeId })
- return renderVersionBadge({ version: data.version })
+ const chromeWebStore = await this.fetch({ storeId })
+ const version = chromeWebStore.version()
+ if (version == null) {
+ throw new NotFound({ prettyMessage: 'not found' })
+ }
+ return renderVersionBadge({ version })
}
}
diff --git a/services/chrome-web-store/chrome-web-store-version.tester.js b/services/chrome-web-store/chrome-web-store-version.tester.js
index 39208bcc0a1a1..f108c7e70827e 100644
--- a/services/chrome-web-store/chrome-web-store-version.tester.js
+++ b/services/chrome-web-store/chrome-web-store-version.tester.js
@@ -1,21 +1,28 @@
-'use strict'
+import { Agent, MockAgent, setGlobalDispatcher } from 'undici'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Version')
- .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .expectBadge({
- label: 'chrome web store',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('Version').get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json').expectBadge({
+ label: 'chrome web store',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('Version (not found)')
.get('/invalid-name-of-addon.json')
.expectBadge({ label: 'chrome web store', message: 'not found' })
// Keep this "inaccessible" test, since this service does not use BaseService#_request.
+const mockAgent = new MockAgent()
t.create('Version (inaccessible)')
.get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json')
- .networkOff()
+ // webextension-store-meta uses undici internally, so we can't mock it with nock
+ .before(function () {
+ setGlobalDispatcher(mockAgent)
+ mockAgent.disableNetConnect()
+ })
+ .after(async function () {
+ await mockAgent.close()
+ setGlobalDispatcher(new Agent())
+ })
.expectBadge({ label: 'chrome web store', message: 'inaccessible' })
diff --git a/services/cii-best-practices/cii-best-practices.service.js b/services/cii-best-practices/cii-best-practices.service.js
index bc7eda1469e1e..ae1263fc2bea7 100644
--- a/services/cii-best-practices/cii-best-practices.service.js
+++ b/services/cii-best-practices/cii-best-practices.service.js
@@ -1,16 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale, coveragePercentage } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { colorScale, coveragePercentage } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
badge_level: Joi.string().required(),
tiered_percentage: Joi.number().required(),
}).required()
-const keywords = ['core infrastructure initiative']
-
const summaryColorScale = colorScale(
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300],
[
@@ -27,60 +23,38 @@ const summaryColorScale = colorScale(
'brightgreen',
'#BBBBBB',
'#E9C504',
- ]
+ ],
)
-module.exports = class CIIBestPracticesService extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'cii',
- pattern: ':metric(level|percentage|summary)/:projectId',
- }
+export default class CIIBestPracticesService extends BaseJsonService {
+ static category = 'analysis'
+ static route = {
+ base: 'cii',
+ pattern: ':metric(level|percentage|summary)/:projectId',
}
- static get examples() {
- return [
- {
- title: 'CII Best Practices Level',
- pattern: 'level/:projectId',
- namedParams: {
- projectId: '1',
- },
- staticPreview: this.renderLevelBadge({ level: 'gold' }),
- keywords,
- },
- {
- title: 'CII Best Practices Tiered Percentage',
- pattern: 'percentage/:projectId',
- namedParams: {
- projectId: '29',
- },
- staticPreview: this.renderTieredPercentageBadge({ percentage: 107 }),
- keywords,
- },
- {
- title: 'CII Best Practices Summary',
- pattern: 'summary/:projectId',
- namedParams: {
- projectId: '33',
- },
- staticPreview: this.renderSummaryBadge({ percentage: 94 }),
- keywords,
- documentation:
- 'This badge uses the same message and color scale as the native CII one, but with all the configuration and goodness that Shields provides!',
+ static openApi = {
+ '/cii/{metric}/{projectId}': {
+ get: {
+ summary: 'CII Best Practices',
+ description:
+ 'The Core Infrastructure Initiative (CII) Best Practices badge is a way for Open Source projects to show that they follow best practices',
+ parameters: pathParams(
+ {
+ name: 'metric',
+ example: 'level',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ },
+ {
+ name: 'projectId',
+ example: '1',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'cii',
- }
- }
+ static defaultBadgeData = { label: 'cii' }
static renderLevelBadge({ level }) {
let message = level
@@ -124,16 +98,14 @@ module.exports = class CIIBestPracticesService extends BaseJsonService {
async handle({ metric, projectId }) {
// No official API documentation is available.
- const {
- badge_level: level,
- tiered_percentage: percentage,
- } = await this._requestJson({
- schema,
- url: `https://bestpractices.coreinfrastructure.org/projects/${projectId}/badge.json`,
- errorMessages: {
- 404: 'project not found',
- },
- })
+ const { badge_level: level, tiered_percentage: percentage } =
+ await this._requestJson({
+ schema,
+ url: `https://bestpractices.coreinfrastructure.org/projects/${projectId}/badge.json`,
+ httpErrors: {
+ 404: 'project not found',
+ },
+ })
if (metric === 'level') {
return this.constructor.renderLevelBadge({ level })
diff --git a/services/cii-best-practices/cii-best-practices.tester.js b/services/cii-best-practices/cii-best-practices.tester.js
index b908976637806..e75a527c7c8cf 100644
--- a/services/cii-best-practices/cii-best-practices.tester.js
+++ b/services/cii-best-practices/cii-best-practices.tester.js
@@ -1,42 +1,41 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('level known project')
- .get(`/level/1.json`)
+ .get('/level/1.json')
.expectBadge({
label: 'cii',
message: withRegex(/in progress|passing|silver|gold/),
})
t.create('percentage known project')
- .get(`/percentage/29.json`)
+ .get('/percentage/29.json')
.expectBadge({
label: 'cii',
message: withRegex(/([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-9][0-9]|300)%/),
})
t.create('summary known project')
- .get(`/summary/33.json`)
+ .get('/summary/33.json')
.expectBadge({
label: 'cii',
message: withRegex(/(in progress [0-9]|[1-9][0-9]%)|passing|silver|gold/),
})
t.create('unknown project')
- .get(`/level/abc.json`)
+ .get('/level/abc.json')
.expectBadge({ label: 'cii', message: 'project not found' })
t.create('level: gold project')
- .get(`/level/1.json`)
+ .get('/level/1.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/1/badge.json')
.reply(200, {
badge_level: 'gold',
tiered_percentage: 300,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -44,14 +43,14 @@ t.create('level: gold project')
})
t.create('level: silver project')
- .get(`/level/34.json`)
+ .get('/level/34.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/34/badge.json')
.reply(200, {
badge_level: 'silver',
tiered_percentage: 297,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -59,14 +58,14 @@ t.create('level: silver project')
})
t.create('level: passing project')
- .get(`/level/29.json`)
+ .get('/level/29.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/29/badge.json')
.reply(200, {
badge_level: 'passing',
tiered_percentage: 107,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -74,14 +73,14 @@ t.create('level: passing project')
})
t.create('level: in progress project')
- .get(`/level/33.json`)
+ .get('/level/33.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/33/badge.json')
.reply(200, {
badge_level: 'in_progress',
tiered_percentage: 94,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -89,14 +88,14 @@ t.create('level: in progress project')
})
t.create('percentage: gold project')
- .get(`/percentage/1.json`)
+ .get('/percentage/1.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/1/badge.json')
.reply(200, {
badge_level: 'gold',
tiered_percentage: 300,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -104,14 +103,14 @@ t.create('percentage: gold project')
})
t.create('percentage: silver project')
- .get(`/percentage/34.json`)
+ .get('/percentage/34.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/34/badge.json')
.reply(200, {
badge_level: 'silver',
tiered_percentage: 297,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -119,14 +118,14 @@ t.create('percentage: silver project')
})
t.create('percentage: passing project')
- .get(`/percentage/29.json`)
+ .get('/percentage/29.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/29/badge.json')
.reply(200, {
badge_level: 'passing',
tiered_percentage: 107,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -134,14 +133,14 @@ t.create('percentage: passing project')
})
t.create('percentage: in progress project')
- .get(`/percentage/33.json`)
+ .get('/percentage/33.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/33/badge.json')
.reply(200, {
badge_level: 'in_progress',
tiered_percentage: 94,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -149,14 +148,14 @@ t.create('percentage: in progress project')
})
t.create('summary: gold project')
- .get(`/summary/1.json`)
+ .get('/summary/1.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/1/badge.json')
.reply(200, {
badge_level: 'gold',
tiered_percentage: 300,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -164,14 +163,14 @@ t.create('summary: gold project')
})
t.create('summary: silver project')
- .get(`/summary/34.json`)
+ .get('/summary/34.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/34/badge.json')
.reply(200, {
badge_level: 'silver',
tiered_percentage: 297,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -179,14 +178,14 @@ t.create('summary: silver project')
})
t.create('summary: passing project')
- .get(`/summary/29.json`)
+ .get('/summary/29.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/29/badge.json')
.reply(200, {
badge_level: 'passing',
tiered_percentage: 107,
- })
+ }),
)
.expectBadge({
label: 'cii',
@@ -194,14 +193,14 @@ t.create('summary: passing project')
})
t.create('summary: in progress project')
- .get(`/summary/33.json`)
+ .get('/summary/33.json')
.intercept(nock =>
nock('https://bestpractices.coreinfrastructure.org/projects')
.get('/33/badge.json')
.reply(200, {
badge_level: 'in_progress',
tiered_percentage: 94,
- })
+ }),
)
.expectBadge({
label: 'cii',
diff --git a/services/circleci/circleci.service.js b/services/circleci/circleci.service.js
index 5979a1bf7aa72..38d9f43f9c87e 100644
--- a/services/circleci/circleci.service.js
+++ b/services/circleci/circleci.service.js
@@ -1,59 +1,92 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService, redirector } = require('..')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import {
+ BaseSvgScrapingService,
+ retiredService,
+ redirector,
+ pathParam,
+ queryParam,
+} from '../index.js'
const circleSchema = Joi.object({ message: isBuildStatus }).required()
const queryParamSchema = Joi.object({ token: Joi.string() }).required()
-const documentation = `
-
- You may specify an optional token to get the status for a private repository.
-
- If you need to use a token, please use a Project Token and only assign your token the 'Status' permission. Never use a Personal Token as they grant full read write permissions to your projects.
-
- For more information about managing Circle CI tokens, please read this article .
-
- `
+const tokenDescription = `
+You may specify an optional token to get the status for a private repository.
+
+If you need to use a token, please use a Project Token and only assign your token the 'Status' permission. Never use a Personal Token as they grant full read write permissions to your projects.
+
+For more information about managing Circle CI tokens, please read this article .
+`
const vcsTypeMap = { gh: 'gh', github: 'gh', bb: 'bb', bitbucket: 'bb' }
class CircleCi extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'circleci/build',
- pattern: ':vcsType(github|gh|bitbucket|bb)/:user/:repo/:branch*',
- queryParamSchema,
- }
+ static category = 'build'
+ static route = {
+ base: 'circleci/build',
+ pattern: ':vcsType(github|gh|bitbucket|bb)/:user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'CircleCI',
- namedParams: {
- vcsType: 'github',
- user: 'RedSparr0w',
- repo: 'node-csgo-parser',
- branch: 'master',
- },
- queryParams: {
- token: 'abc123def456',
- },
- staticPreview: this.render({ status: 'success' }),
- documentation,
+ static openApi = {
+ '/circleci/build/{vcsType}/{user}/{repo}': {
+ get: {
+ summary: 'CircleCI',
+ parameters: [
+ pathParam({
+ name: 'vcsType',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ example: 'github',
+ }),
+ pathParam({
+ name: 'user',
+ example: 'RedSparr0w',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'node-csgo-parser',
+ }),
+ queryParam({
+ name: 'token',
+ example: 'abc123def456',
+ description: tokenDescription,
+ }),
+ ],
},
- ]
+ },
+ '/circleci/build/{vcsType}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'CircleCI (branch)',
+ parameters: [
+ pathParam({
+ name: 'vcsType',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ example: 'github',
+ }),
+ pathParam({
+ name: 'user',
+ example: 'RedSparr0w',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'node-csgo-parser',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ queryParam({
+ name: 'token',
+ example: 'abc123def456',
+ description: tokenDescription,
+ }),
+ ],
+ },
+ },
}
- static get defaultBadgeData() {
- return { label: 'build' }
- }
+ static defaultBadgeData = { label: 'build' }
static render({ status }) {
return renderBuildStatusBadge({ status: status.replace('_', ' ') })
@@ -67,29 +100,24 @@ class CircleCi extends BaseSvgScrapingService {
url: `https://circleci.com/${vcs}/${user}/${repo}${branchClause}.svg`,
// Note that the unusual 'circle-token' query param name is required.
// https://circleci.com/docs/api/#get-authenticated
- options: { qs: { style: 'shield', 'circle-token': token } },
- errorMessages: { 404: 'project not found' },
+ options: { searchParams: { style: 'shield', 'circle-token': token } },
+ httpErrors: { 404: 'project not found' },
})
return this.constructor.render({ status: message })
}
}
const legacyRoutes = [
- redirector({
+ retiredService({
category: 'build',
+ label: 'circleci',
route: {
base: 'circleci/token',
pattern:
':token/project/:vcsType(github|bitbucket)?/:user/:repo/:branch*',
},
- transformPath: ({ vcsType, user, repo, branch }) => {
- const vcs = vcsType || 'gh'
- return `/circleci/build/${vcs}/${user}/${repo}${
- branch ? `/${branch}` : ''
- }`
- },
- transformQueryParams: ({ token }) => ({ token }),
- dateAdded: new Date('2019-05-05'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
redirector({
category: 'build',
@@ -107,4 +135,4 @@ const legacyRoutes = [
}),
]
-module.exports = [...legacyRoutes, CircleCi]
+export default { ...legacyRoutes, CircleCi }
diff --git a/services/circleci/circleci.tester.js b/services/circleci/circleci.tester.js
index ecdded2b10e9a..5820d69c9dc2d 100644
--- a/services/circleci/circleci.tester.js
+++ b/services/circleci/circleci.tester.js
@@ -1,12 +1,10 @@
-'use strict'
+import { isBuildStatus } from '../build-status.js'
+import { ServiceTester } from '../tester.js'
-const { isBuildStatus } = require('../build-status')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'circleci',
title: 'Circle CI',
-}))
+})
t.create('circle ci (valid, without branch)')
.get('/build/gh/RedSparr0w/node-csgo-parser.json')
@@ -35,7 +33,7 @@ t.create('circle ci (not found)')
t.create('circle ci (valid, with token)')
.get(
- '/build/gh/RedSparr0w/node-csgo-parser/master.json?token=b90b5c49e59a4c67ba3a92f7992587ac7a0408c2'
+ '/build/gh/justkd/uidmanager/main.json?token=2edcfec5c13eaf6d951a8f2939b220cdca74644c',
)
.expectBadge({
label: 'build',
@@ -52,16 +50,18 @@ t.create('legacy route (assume "github" as a default VCS)')
t.create('legacy route with token and VCS')
.get(
- '/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/github/RedSparr0w/node-csgo-parser/master.svg'
- )
- .expectRedirect(
- '/circleci/build/github/redsparr0w/node-csgo-parser/master.svg?token=b90b5c49e59a4c67ba3a92f7992587ac7a0408c2'
+ '/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/github/RedSparr0w/node-csgo-parser/master.json',
)
+ .expectBadge({
+ label: 'circleci',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('legacy route with token (assume "github" as a default VCS)')
.get(
- '/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/RedSparr0w/node-csgo-parser/master.svg'
- )
- .expectRedirect(
- '/circleci/build/gh/redsparr0w/node-csgo-parser/master.svg?token=b90b5c49e59a4c67ba3a92f7992587ac7a0408c2'
+ '/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/RedSparr0w/node-csgo-parser/master.json',
)
+ .expectBadge({
+ label: 'circleci',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/cirrus/cirrus.service.js b/services/cirrus/cirrus.service.js
index a773d9809daa5..225ff94724f38 100644
--- a/services/cirrus/cirrus.service.js
+++ b/services/cirrus/cirrus.service.js
@@ -1,84 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- subject: Joi.string().required(),
- status: Joi.alternatives()
- .try(isBuildStatus, Joi.equal('unknown'))
- .required(),
-}).required()
-const queryParamSchema = Joi.object({
- task: Joi.string(),
- script: Joi.string(),
-}).required()
-
-module.exports = class Cirrus extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'cirrus',
- pattern: 'github/:user/:repo/:branch*',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Cirrus CI - Base Branch Build Status',
- namedParams: { user: 'flutter', repo: 'flutter' },
- pattern: 'github/:user/:repo',
- queryParams: { task: 'analyze', script: 'test' },
- staticPreview: this.render({ status: 'passing' }),
- },
- {
- title: 'Cirrus CI - Specific Branch Build Status',
- pattern: 'github/:user/:repo/:branch',
- namedParams: { user: 'flutter', repo: 'flutter', branch: 'master' },
- queryParams: { task: 'analyze', script: 'test' },
- staticPreview: this.render({ status: 'passing' }),
- },
- {
- title: 'Cirrus CI - Specific Task Build Status',
- pattern: 'github/:user/:repo',
- queryParams: { task: 'analyze' },
- namedParams: { user: 'flutter', repo: 'flutter' },
- staticPreview: this.render({ subject: 'analyze', status: 'passing' }),
- },
- {
- title: 'Cirrus CI - Task and Script Build Status',
- pattern: 'github/:user/:repo',
- queryParams: { task: 'analyze', script: 'test' },
- namedParams: {
- user: 'flutter',
- repo: 'flutter',
- },
- staticPreview: this.render({ subject: 'test', status: 'passing' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'build' }
- }
-
- static render({ subject, status }) {
- return renderBuildStatusBadge({ label: subject, status })
- }
-
- async handle({ user, repo, branch }, { script, task }) {
- const json = await this._requestJson({
- schema,
- url: `https://api.cirrus-ci.com/github/${user}/${repo}.json`,
- options: { qs: { branch, script, task } },
- })
-
- return this.constructor.render(json)
- }
-}
+import { retiredService } from '../index.js'
+
+export const Cirrus = retiredService({
+ category: 'build',
+ label: 'build',
+ route: {
+ base: 'cirrus',
+ pattern: ':various+',
+ },
+ dateAdded: new Date('2026-06-13'),
+})
diff --git a/services/cirrus/cirrus.tester.js b/services/cirrus/cirrus.tester.js
index b3f1cd8c90ba3..defae9390ecc1 100644
--- a/services/cirrus/cirrus.tester.js
+++ b/services/cirrus/cirrus.tester.js
@@ -1,16 +1,19 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = new ServiceTester({
+ id: 'cirrus',
+ title: 'Cirrus CI',
+ pathPrefix: '/cirrus',
+})
-t.create('cirrus bad repo')
- .get('/github/unknown-identifier/unknown-repo.json')
- .expectBadge({ label: 'build', message: 'unknown' })
+t.create('default branch').get('/github/flutter/flutter.json').expectBadge({
+ label: 'build',
+ message: 'retired badge',
+})
-t.create('cirrus fully.valid')
- .get('/github/flutter/flutter.json')
+t.create('specific branch')
+ .get('/github/flutter/flutter/master.json')
.expectBadge({
label: 'build',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
+ message: 'retired badge',
})
diff --git a/services/clearlydefined/clearlydefined-score.service.js b/services/clearlydefined/clearlydefined-score.service.js
new file mode 100644
index 0000000000000..2d971bba0695e
--- /dev/null
+++ b/services/clearlydefined/clearlydefined-score.service.js
@@ -0,0 +1,98 @@
+import Joi from 'joi'
+import {
+ nonNegativeInteger,
+ optionalNonNegativeInteger,
+} from '../validators.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { BaseService, NotFound, pathParams } from '../index.js'
+import { parseJson } from '../../core/base-service/json.js'
+
+const schema = Joi.object({
+ scores: Joi.object({
+ effective: nonNegativeInteger,
+ }).required(),
+ described: Joi.object({
+ files: optionalNonNegativeInteger,
+ }),
+}).required()
+
+// This service based on the REST API for clearlydefined.io
+// https://api.clearlydefined.io/api-docs/
+export default class ClearlyDefinedService extends BaseService {
+ static category = 'analysis'
+ static route = {
+ base: 'clearlydefined',
+ pattern: 'score/:type/:provider/:namespace/:name/:revision',
+ }
+
+ static openApi = {
+ '/clearlydefined/score/{type}/{provider}/{namespace}/{name}/{revision}': {
+ get: {
+ summary: 'ClearlyDefined Score',
+ parameters: pathParams(
+ {
+ name: 'type',
+ example: 'npm',
+ },
+ {
+ name: 'provider',
+ example: 'npmjs',
+ },
+ {
+ name: 'namespace',
+ example: '-',
+ },
+ {
+ name: 'name',
+ example: 'jquery',
+ },
+ {
+ name: 'revision',
+ example: '3.4.1',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'score' }
+
+ static render({ score }) {
+ score = Math.round(score)
+ return {
+ label: 'score',
+ message: `${score}/100`,
+ color: floorCountColor(score, 40, 60, 100),
+ }
+ }
+
+ async fetch({ type, provider, namespace, name, revision }) {
+ const { buffer } = await this._request({
+ url: `https://api.clearlydefined.io/definitions/${type}/${provider}/${namespace}/${name}/${revision}`,
+ options: { headers: { Accept: 'application/json' } },
+ })
+ // If the type or provider is not found, the API returns a 200 response
+ // with an empty body. It cannot be parsed as JSON, we need to handle this
+ // case earlier than in the usual BaseJsonService._requestJson flow.
+ if (buffer.length === 0) {
+ throw new NotFound({
+ prettyMessage: 'unknown type, provider, or upstream issue',
+ })
+ }
+ const json = parseJson(buffer)
+ return this.constructor._validate(json, schema)
+ }
+
+ async handle({ type, provider, namespace, name, revision }) {
+ const data = await this.fetch({ type, provider, namespace, name, revision })
+ // Return score only if definition contains some files,
+ // else it was an incomplete response due to unknown coordinates
+ if (data.described.files > 0) {
+ return this.constructor.render({ score: data.scores.effective })
+ } else {
+ throw new NotFound({
+ prettyMessage: 'unknown namespace, name, or revision',
+ })
+ }
+ }
+}
diff --git a/services/clearlydefined/clearlydefined-score.tester.js b/services/clearlydefined/clearlydefined-score.tester.js
new file mode 100644
index 0000000000000..b24d9ec8f21b4
--- /dev/null
+++ b/services/clearlydefined/clearlydefined-score.tester.js
@@ -0,0 +1,45 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('ClearlyDefined Score')
+ .get('/score/npm/npmjs/-/jquery/3.4.1.json')
+ .expectBadge({
+ label: 'score',
+ message: Joi.string().regex(/^\d+\/\d+$/),
+ })
+
+t.create('ClearlyDefined Score (revision not found)')
+ .get('/score/npm/npmjs/-/jquery/999.1.1.json')
+ .expectBadge({
+ label: 'score',
+ message: 'unknown namespace, name, or revision',
+ })
+
+t.create('ClearlyDefined Score (name not found)')
+ .get('/score/npm/npmjs/-/not-a-real-package/3.4.1.json')
+ .expectBadge({
+ label: 'score',
+ message: 'unknown namespace, name, or revision',
+ })
+
+t.create('ClearlyDefined Score (namespace not found)')
+ .get('/score/npm/npmjs/not-a-real-namespace/jquery/3.4.1.json')
+ .expectBadge({
+ label: 'score',
+ message: 'unknown namespace, name, or revision',
+ })
+
+t.create('ClearlyDefined Score (provider not found)')
+ .get('/score/npm/not-a-real-provider/-/jquery/3.4.1.json')
+ .expectBadge({
+ label: 'score',
+ message: 'unknown type, provider, or upstream issue',
+ })
+
+t.create('ClearlyDefined Score (type not found)')
+ .get('/score/not-a-real-type/npmjs/-/jquery/3.4.1.json')
+ .expectBadge({
+ label: 'score',
+ message: 'unknown type, provider, or upstream issue',
+ })
diff --git a/services/clojars/clojars-base.js b/services/clojars/clojars-base.js
index 75d79b830bb00..a83cc32055217 100644
--- a/services/clojars/clojars-base.js
+++ b/services/clojars/clojars-base.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
const clojarsSchema = Joi.object({
downloads: nonNegativeInteger,
@@ -21,4 +19,7 @@ class BaseClojarsService extends BaseJsonService {
}
}
-module.exports = { BaseClojarsService }
+const description =
+ '[Clojars](https://clojars.org/) is a repository for Clojure libraries'
+
+export { BaseClojarsService, description }
diff --git a/services/clojars/clojars-downloads.service.js b/services/clojars/clojars-downloads.service.js
index 06ff1b16683de..d3672bec9ec86 100644
--- a/services/clojars/clojars-downloads.service.js
+++ b/services/clojars/clojars-downloads.service.js
@@ -1,44 +1,28 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseClojarsService, description } from './clojars-base.js'
-const { metric } = require('../text-formatters')
-const { downloadCount: downloadsColor } = require('../color-formatters')
-const { BaseClojarsService } = require('./clojars-base')
+export default class ClojarsDownloads extends BaseClojarsService {
+ static category = 'downloads'
+ static route = { base: 'clojars/dt', pattern: ':clojar+' }
-module.exports = class ClojarsDownloads extends BaseClojarsService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'clojars/dt',
- pattern: ':clojar+',
- }
- }
-
- static get examples() {
- return [
- {
- namedParams: { clojar: 'prismic' },
- staticPreview: this.render({ downloads: 117 }),
+ static openApi = {
+ '/clojars/dt/{clojar}': {
+ get: {
+ summary: 'Clojars Downloads',
+ description,
+ parameters: pathParams({
+ name: 'clojar',
+ example: 'prismic',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
-
- static render({ downloads }) {
- return {
- label: 'downloads',
- message: metric(downloads),
- color: downloadsColor(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
async handle({ clojar }) {
const json = await this.fetch({ clojar })
- return this.constructor.render({ downloads: json.downloads })
+ return renderDownloadsBadge({ downloads: json.downloads })
}
}
diff --git a/services/clojars/clojars-downloads.tester.js b/services/clojars/clojars-downloads.tester.js
index 8331f433bcdf5..afb116f46d3e0 100644
--- a/services/clojars/clojars-downloads.tester.js
+++ b/services/clojars/clojars-downloads.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('clojars downloads (valid)')
- .get('/prismic.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('clojars downloads (valid)').get('/prismic.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('clojars downloads (not found)')
.get('/not-a-package.json')
diff --git a/services/clojars/clojars-version.service.js b/services/clojars/clojars-version.service.js
index b89e5e025350d..beb3ed8c15bc8 100644
--- a/services/clojars/clojars-version.service.js
+++ b/services/clojars/clojars-version.service.js
@@ -1,48 +1,43 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { version: versionColor } = require('../color-formatters')
-const { redirector } = require('..')
-const { BaseClojarsService } = require('./clojars-base')
+import Joi from 'joi'
+import { version as versionColor } from '../color-formatters.js'
+import { retiredService, pathParam, queryParam } from '../index.js'
+import { BaseClojarsService, description } from './clojars-base.js'
const queryParamSchema = Joi.object({
include_prereleases: Joi.equal(''),
}).required()
class ClojarsVersionService extends BaseClojarsService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'clojars/v',
- pattern: ':clojar+',
- queryParamSchema,
- }
- }
+ static category = 'version'
+ static route = { base: 'clojars/v', pattern: ':clojar+', queryParamSchema }
- static get examples() {
- return [
- {
- title: 'Clojars Version',
- namedParams: { clojar: 'prismic' },
- staticPreview: this.render({ clojar: 'clojar', version: '1.2' }),
+ static openApi = {
+ '/clojars/v/{clojar}': {
+ get: {
+ summary: 'Clojars Version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'clojar',
+ example: 'prismic',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
},
- {
- title: 'Clojars Version (including pre-releases)',
- namedParams: { clojar: 'prismic' },
- queryParams: { include_prereleases: null },
- staticPreview: this.render({ clojar: 'clojar', version: '1.2' }),
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'clojars' }
- }
+ static defaultBadgeData = { label: 'clojars' }
static render({ clojar, version }) {
+ // clojars format is non standard to fit community style
+ // dont use renderVersionBadge
+ // see also https://github.com/badges/shields/pull/431
+ // commit d0414c9
return {
message: `[${clojar} "${version}"]`,
color: versionColor(version),
@@ -63,17 +58,15 @@ class ClojarsVersionService extends BaseClojarsService {
}
}
-const ClojarsVersionRedirector = redirector({
+const ClojarsVersionRedirector = retiredService({
category: 'version',
+ label: 'clojars',
route: {
base: 'clojars/vpre',
pattern: ':clojar',
},
- transformPath: ({ clojar }) => `/clojars/v/${clojar}`,
- transformQueryParams: params => ({
- include_prereleases: null,
- }),
- dateAdded: new Date('2019-12-15'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
})
-module.exports = { ClojarsVersionService, ClojarsVersionRedirector }
+export { ClojarsVersionService, ClojarsVersionRedirector }
diff --git a/services/clojars/clojars-version.tester.js b/services/clojars/clojars-version.tester.js
index ee495b9c7e77a..0a144620ab8ac 100644
--- a/services/clojars/clojars-version.tester.js
+++ b/services/clojars/clojars-version.tester.js
@@ -1,11 +1,9 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'ClojarsVersion',
title: 'Clojars Version',
pathPrefix: '/clojars',
-}))
+})
t.create('clojars version (valid)')
.get('/v/prismic.json')
@@ -26,5 +24,8 @@ t.create('clojars version (not found)')
.expectBadge({ label: 'clojars', message: 'not found' })
t.create('version (legacy redirect: vpre)')
- .get('/vpre/prismic.svg')
- .expectRedirect('/clojars/v/prismic.svg?include_prereleases')
+ .get('/vpre/prismic.json')
+ .expectBadge({
+ label: 'clojars',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/cocoapods/cocoapods-apps.service.js b/services/cocoapods/cocoapods-apps.service.js
deleted file mode 100644
index 277cc6a43cdef..0000000000000
--- a/services/cocoapods/cocoapods-apps.service.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- name: 'CocoapodsApps',
- category: 'other',
- route: {
- base: 'cocoapods',
- pattern: ':interval(aw|at)/:spec',
- },
- label: 'apps',
- dateAdded: new Date('2018-01-06'),
-})
diff --git a/services/cocoapods/cocoapods-apps.tester.js b/services/cocoapods/cocoapods-apps.tester.js
deleted file mode 100644
index 07c8e86521161..0000000000000
--- a/services/cocoapods/cocoapods-apps.tester.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
- id: 'CocoapodsApps',
- title: 'CocoapodsApps',
- pathPrefix: '/cocoapods',
-}))
-
-t.create('apps (valid, weekly)')
- .get('/aw/AFNetworking.json')
- .expectBadge({ label: 'apps', message: 'no longer available' })
-
-t.create('apps (valid, total)')
- .get('/at/AFNetworking.json')
- .expectBadge({ label: 'apps', message: 'no longer available' })
-
-t.create('apps (not found)')
- .get('/at/not-a-package.json')
- .expectBadge({ label: 'apps', message: 'no longer available' })
diff --git a/services/cocoapods/cocoapods-base.js b/services/cocoapods/cocoapods-base.js
index a47f9c069314f..2a5a935106f13 100644
--- a/services/cocoapods/cocoapods-base.js
+++ b/services/cocoapods/cocoapods-base.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
version: Joi.string().required(),
@@ -10,13 +8,13 @@ const schema = Joi.object({
Joi.string().required(),
Joi.object({
type: Joi.string().required(),
- }).required()
+ }).required(),
),
// https://github.com/badges/shields/pull/209
platforms: Joi.object().default({ ios: '5.0', osx: '10.7' }),
}).required()
-module.exports = class BaseCocoaPodsService extends BaseJsonService {
+export default class BaseCocoaPodsService extends BaseJsonService {
async fetch({ spec }) {
return this._requestJson({
schema,
diff --git a/services/cocoapods/cocoapods-docs.service.js b/services/cocoapods/cocoapods-docs.service.js
index 002ba6918ed4e..20bdd8b57c304 100644
--- a/services/cocoapods/cocoapods-docs.service.js
+++ b/services/cocoapods/cocoapods-docs.service.js
@@ -1,44 +1,30 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- coveragePercentage: coveragePercentageColor,
-} = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
cocoadocs: Joi.object({
- doc_percent: Joi.number()
- .allow(null)
- .required(),
+ doc_percent: Joi.number().allow(null).required(),
}).required(),
}).required()
-module.exports = class CocoapodsDocs extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'cocoapods/metrics/doc-percent',
- pattern: ':spec',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Cocoapods doc percentage',
- namedParams: { spec: 'AFNetworking' },
- staticPreview: this.render({ percentage: 94 }),
+export default class CocoapodsDocs extends BaseJsonService {
+ static category = 'analysis'
+ static route = { base: 'cocoapods/metrics/doc-percent', pattern: ':spec' }
+
+ static openApi = {
+ '/cocoapods/metrics/doc-percent/{spec}': {
+ get: {
+ summary: 'Cocoapods doc percentage',
+ parameters: pathParams({
+ name: 'spec',
+ example: 'AFNetworking',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'docs' }
- }
+ static defaultBadgeData = { label: 'docs' }
static render({ percentage }) {
return {
diff --git a/services/cocoapods/cocoapods-docs.tester.js b/services/cocoapods/cocoapods-docs.tester.js
index 8c78313163c70..65e49f21690db 100644
--- a/services/cocoapods/cocoapods-docs.tester.js
+++ b/services/cocoapods/cocoapods-docs.tester.js
@@ -1,21 +1,18 @@
-'use strict'
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('doc percent (valid)')
- .get('/AFNetworking.json')
- .expectBadge({
- label: 'docs',
- message: isIntegerPercentage,
- })
+t.create('doc percent (valid)').get('/AFNetworking.json').expectBadge({
+ label: 'docs',
+ message: isIntegerPercentage,
+})
t.create('doc percent (null)')
.get('/AFNetworking.json')
.intercept(nock =>
nock('https://metrics.cocoapods.org')
.get('/api/v1/pods/AFNetworking')
- .reply(200, '{"cocoadocs": {"doc_percent": null}}')
+ .reply(200, '{"cocoadocs": {"doc_percent": null}}'),
)
.expectBadge({ label: 'docs', message: '0%' })
diff --git a/services/cocoapods/cocoapods-downloads.service.js b/services/cocoapods/cocoapods-downloads.service.js
deleted file mode 100644
index c0caf8da6be81..0000000000000
--- a/services/cocoapods/cocoapods-downloads.service.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- name: 'CocoapodsDownloads',
- category: 'downloads',
- route: {
- base: 'cocoapods',
- pattern: ':interval(dm|dw|dt)/:spec',
- },
- label: 'downloads',
- dateAdded: new Date('2018-01-06'),
-})
diff --git a/services/cocoapods/cocoapods-downloads.tester.js b/services/cocoapods/cocoapods-downloads.tester.js
deleted file mode 100644
index 8e66b7470ed5b..0000000000000
--- a/services/cocoapods/cocoapods-downloads.tester.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'CocoapodsDownloads',
- title: 'CocoapodsDownloads',
- pathPrefix: '/cocoapods',
-}))
-
-t.create('downloads (valid, monthly)')
- .get('/dm/AFNetworking.json')
- .expectBadge({ label: 'downloads', message: 'no longer available' })
-
-t.create('downloads (valid, weekly)')
- .get('/dw/AFNetworking.json')
- .expectBadge({ label: 'downloads', message: 'no longer available' })
-
-t.create('downloads (valid, total)')
- .get('/dt/AFNetworking.json')
- .expectBadge({ label: 'downloads', message: 'no longer available' })
-
-t.create('downloads (not found)')
- .get('/dt/not-a-package.json')
- .expectBadge({ label: 'downloads', message: 'no longer available' })
diff --git a/services/cocoapods/cocoapods-license.service.js b/services/cocoapods/cocoapods-license.service.js
index cf2b17a29ed08..d9d84374bd852 100644
--- a/services/cocoapods/cocoapods-license.service.js
+++ b/services/cocoapods/cocoapods-license.service.js
@@ -1,32 +1,23 @@
-'use strict'
+import { pathParams } from '../index.js'
+import BaseCocoaPodsService from './cocoapods-base.js'
-const BaseCocoaPodsService = require('./cocoapods-base')
+export default class CocoapodsLicense extends BaseCocoaPodsService {
+ static category = 'license'
+ static route = { base: 'cocoapods/l', pattern: ':spec' }
-module.exports = class CocoapodsLicense extends BaseCocoaPodsService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'cocoapods/l',
- pattern: ':spec',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Cocoapods',
- namedParams: { spec: 'AFNetworking' },
- staticPreview: this.render({ license: 'MIT' }),
+ static openApi = {
+ '/cocoapods/l/{spec}': {
+ get: {
+ summary: 'Cocoapods License',
+ parameters: pathParams({
+ name: 'spec',
+ example: 'AFNetworking',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ license }) {
if (license) {
diff --git a/services/cocoapods/cocoapods-license.tester.js b/services/cocoapods/cocoapods-license.tester.js
index 3f6c633192482..6be0867f9eb33 100644
--- a/services/cocoapods/cocoapods-license.tester.js
+++ b/services/cocoapods/cocoapods-license.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license (valid)')
.get('/AFNetworking.json')
@@ -14,7 +13,7 @@ t.create('missing license')
.reply(200, {
version: '1.0.7',
platforms: { ios: '8.0' },
- })
+ }),
)
.expectBadge({ label: 'license', message: 'not specified' })
diff --git a/services/cocoapods/cocoapods-platform.service.js b/services/cocoapods/cocoapods-platform.service.js
index 71ddac3193f4b..c415aadbf4227 100644
--- a/services/cocoapods/cocoapods-platform.service.js
+++ b/services/cocoapods/cocoapods-platform.service.js
@@ -1,34 +1,23 @@
-'use strict'
+import { pathParams } from '../index.js'
+import BaseCocoaPodsService from './cocoapods-base.js'
-const BaseCocoaPodsService = require('./cocoapods-base')
+export default class CocoapodsPlatform extends BaseCocoaPodsService {
+ static category = 'platform-support'
+ static route = { base: 'cocoapods/p', pattern: ':spec' }
-module.exports = class CocoapodsPlatform extends BaseCocoaPodsService {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return {
- base: 'cocoapods/p',
- pattern: ':spec',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Cocoapods platforms',
- namedParams: { spec: 'AFNetworking' },
- staticPreview: this.render({
- platforms: ['ios', 'osx', 'watchos', 'tvos'],
+ static openApi = {
+ '/cocoapods/p/{spec}': {
+ get: {
+ summary: 'Cocoapods platforms',
+ parameters: pathParams({
+ name: 'spec',
+ example: 'AFNetworking',
}),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'platform' }
- }
+ static defaultBadgeData = { label: 'platform' }
static render({ platforms }) {
return {
diff --git a/services/cocoapods/cocoapods-platform.tester.js b/services/cocoapods/cocoapods-platform.tester.js
index d8ffe1672363d..6d92fe089bdc1 100644
--- a/services/cocoapods/cocoapods-platform.tester.js
+++ b/services/cocoapods/cocoapods-platform.tester.js
@@ -1,18 +1,15 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isPlatform = Joi.string().regex(
- /^(osx|ios|tvos|watchos)( \| (osx|ios|tvos|watchos))*$/
+ /^(osx|ios|tvos|watchos)( \| (osx|ios|tvos|watchos))*$/,
)
-t.create('platform (valid)')
- .get('/AFNetworking.json')
- .expectBadge({
- label: 'platform',
- message: isPlatform,
- })
+t.create('platform (valid)').get('/AFNetworking.json').expectBadge({
+ label: 'platform',
+ message: isPlatform,
+})
t.create('platform (not found)')
.get('/not-a-package.json')
@@ -23,6 +20,6 @@ t.create('platform (missing platforms key)')
.intercept(nock =>
nock('https://trunk.cocoapods.org')
.get('/api/v1/pods/AFNetworking/specs/latest')
- .reply(200, { version: 'v1.0', license: 'MIT' })
+ .reply(200, { version: 'v1.0', license: 'MIT' }),
)
.expectBadge({ label: 'platform', message: 'ios | osx' })
diff --git a/services/cocoapods/cocoapods-version.service.js b/services/cocoapods/cocoapods-version.service.js
index 7235324226d30..b5d79226b6e4a 100644
--- a/services/cocoapods/cocoapods-version.service.js
+++ b/services/cocoapods/cocoapods-version.service.js
@@ -1,33 +1,24 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import BaseCocoaPodsService from './cocoapods-base.js'
-const { renderVersionBadge } = require('../version')
-const BaseCocoaPodsService = require('./cocoapods-base')
+export default class CocoapodsVersion extends BaseCocoaPodsService {
+ static category = 'version'
+ static route = { base: 'cocoapods/v', pattern: ':spec' }
-module.exports = class CocoapodsVersion extends BaseCocoaPodsService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'cocoapods/v',
- pattern: ':spec',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Cocoapods',
- namedParams: { spec: 'AFNetworking' },
- staticPreview: renderVersionBadge({ version: 'v3.2.1' }),
+ static openApi = {
+ '/cocoapods/v/{spec}': {
+ get: {
+ summary: 'Cocoapods Version',
+ parameters: pathParams({
+ name: 'spec',
+ example: 'AFNetworking',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'pod' }
- }
+ static defaultBadgeData = { label: 'pod' }
async handle({ spec }) {
const { version } = await this.fetch({ spec })
diff --git a/services/cocoapods/cocoapods-version.tester.js b/services/cocoapods/cocoapods-version.tester.js
index 628037601901a..e4e934abf5693 100644
--- a/services/cocoapods/cocoapods-version.tester.js
+++ b/services/cocoapods/cocoapods-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('version (valid)')
- .get('/AFNetworking.json')
- .expectBadge({
- label: 'pod',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('version (valid)').get('/AFNetworking.json').expectBadge({
+ label: 'pod',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('version (not found)')
.get('/not-a-package.json')
diff --git a/services/codacy/codacy-coverage.service.js b/services/codacy/codacy-coverage.service.js
index 6cd3f0bbf0a51..0b884a2e79316 100644
--- a/services/codacy/codacy-coverage.service.js
+++ b/services/codacy/codacy-coverage.service.js
@@ -1,11 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- coveragePercentage: coveragePercentageColor,
-} = require('../color-formatters')
-const { BaseSvgScrapingService } = require('..')
-const { NotFound } = require('..')
+import Joi from 'joi'
+import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js'
+import { BaseSvgScrapingService, NotFound, pathParams } from '../index.js'
const schema = Joi.object({
message: Joi.alternatives()
@@ -13,43 +8,38 @@ const schema = Joi.object({
.required(),
}).required()
-module.exports = class CodacyCoverage extends BaseSvgScrapingService {
- static get category() {
- return 'coverage'
- }
+export default class CodacyCoverage extends BaseSvgScrapingService {
+ static category = 'coverage'
+ static route = { base: 'codacy/coverage', pattern: ':projectId/:branch*' }
- static get route() {
- return {
- base: 'codacy/coverage',
- pattern: ':projectId/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Codacy coverage',
- pattern: ':projectId',
- namedParams: { projectId: '59d607d0e311408885e418004068ea58' },
- staticPreview: this.render({ percentage: 90 }),
+ static openApi = {
+ '/codacy/coverage/{projectId}': {
+ get: {
+ summary: 'Codacy coverage',
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'b6a59cdf5ca64eab9104928d4f9bbb97',
+ }),
},
- {
- title: 'Codacy branch coverage',
- pattern: ':projectId/:branch',
- namedParams: {
- projectId: '59d607d0e311408885e418004068ea58',
- branch: 'master',
- },
- staticPreview: this.render({ percentage: 90 }),
+ },
+ '/codacy/coverage/{projectId}/{branch}': {
+ get: {
+ summary: 'Codacy coverage (branch)',
+ parameters: pathParams(
+ {
+ name: 'projectId',
+ example: 'b6a59cdf5ca64eab9104928d4f9bbb97',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'coverage',
- }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ percentage }) {
return {
@@ -68,11 +58,11 @@ module.exports = class CodacyCoverage extends BaseSvgScrapingService {
const { message: coverageString } = await this._requestSvg({
schema,
url: `https://api.codacy.com/project/badge/coverage/${encodeURIComponent(
- projectId
+ projectId,
)}`,
- options: { qs: { branch } },
+ options: { searchParams: { branch } },
valueMatcher: /text-anchor="middle">([^<>]+)<\/text>/,
- errorMessages: {
+ httpErrors: {
404: 'project not found',
},
})
diff --git a/services/codacy/codacy-coverage.tester.js b/services/codacy/codacy-coverage.tester.js
index c159f424754f5..8f8b3f1c0bdcf 100644
--- a/services/codacy/codacy-coverage.tester.js
+++ b/services/codacy/codacy-coverage.tester.js
@@ -1,24 +1,23 @@
-'use strict'
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Coverage')
- .get('/59d607d0e311408885e418004068ea58.json')
- .expectBadge({
- label: 'coverage',
- message: isIntegerPercentage,
- })
+// https://app.codacy.com/gh/schemacrawler/SchemaCrawler/dashboard
+// https://github.com/schemacrawler/SchemaCrawler
+t.create('Coverage').get('/b6a59cdf5ca64eab9104928d4f9bbb97.json').expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+})
t.create('Coverage on branch')
- .get('/59d607d0e311408885e418004068ea58/master.json')
+ .get('/b6a59cdf5ca64eab9104928d4f9bbb97/master.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('Coverage not enabled')
- .get('/e27821fb6289410b8f58338c7e0bc686.json')
+ .get('/1cc53e72c9fa4d87be03d6085451cb2c.json')
.expectBadge({
label: 'coverage',
message: 'not enabled for this project',
diff --git a/services/codacy/codacy-grade.service.js b/services/codacy/codacy-grade.service.js
index 337a835ae4692..c75a84f361060 100644
--- a/services/codacy/codacy-grade.service.js
+++ b/services/codacy/codacy-grade.service.js
@@ -1,48 +1,41 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseSvgScrapingService } = require('..')
-const { codacyGrade } = require('./codacy-helpers')
+import Joi from 'joi'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
+import { codacyGrade } from './codacy-helpers.js'
const schema = Joi.object({ message: codacyGrade }).required()
-module.exports = class CodacyGrade extends BaseSvgScrapingService {
- static get category() {
- return 'analysis'
- }
+export default class CodacyGrade extends BaseSvgScrapingService {
+ static category = 'analysis'
+ static route = { base: 'codacy/grade', pattern: ':projectId/:branch*' }
- static get route() {
- return {
- base: 'codacy/grade',
- pattern: ':projectId/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Codacy grade',
- pattern: ':projectId',
- namedParams: { projectId: 'e27821fb6289410b8f58338c7e0bc686' },
- staticPreview: this.render({ grade: 'A' }),
+ static openApi = {
+ '/codacy/grade/{projectId}': {
+ get: {
+ summary: 'Codacy grade',
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'b6a59cdf5ca64eab9104928d4f9bbb97',
+ }),
},
- {
- title: 'Codacy branch grade',
- pattern: ':projectId/:branch',
- namedParams: {
- projectId: 'e27821fb6289410b8f58338c7e0bc686',
- branch: 'master',
- },
- staticPreview: this.render({ grade: 'A' }),
+ },
+ '/codacy/grade/{projectId}/{branch}': {
+ get: {
+ summary: 'Codacy grade (branch)',
+ parameters: pathParams(
+ {
+ name: 'projectId',
+ example: 'b6a59cdf5ca64eab9104928d4f9bbb97',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'code quality',
- }
- }
+ static defaultBadgeData = { label: 'code quality' }
static render({ grade }) {
const color = {
@@ -64,10 +57,10 @@ module.exports = class CodacyGrade extends BaseSvgScrapingService {
const { message: grade } = await this._requestSvg({
schema,
url: `https://api.codacy.com/project/badge/grade/${encodeURIComponent(
- projectId
+ projectId,
)}`,
- options: { qs: { branch } },
- errorMessages: { 404: 'project or branch not found' },
+ options: { searchParams: { branch } },
+ httpErrors: { 404: 'project or branch not found' },
valueMatcher: /visibility="hidden">([^<>]+)<\/text>/,
})
return this.constructor.render({ grade })
diff --git a/services/codacy/codacy-grade.tester.js b/services/codacy/codacy-grade.tester.js
index cc8020e2104de..57618fac14938 100644
--- a/services/codacy/codacy-grade.tester.js
+++ b/services/codacy/codacy-grade.tester.js
@@ -1,17 +1,18 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { codacyGrade } = require('./codacy-helpers')
+import { createServiceTester } from '../tester.js'
+import { codacyGrade } from './codacy-helpers.js'
+export const t = await createServiceTester()
+// https://app.codacy.com/gh/NicolasCARPi/jquery_jeditable/dashboard
+// https://github.com/NicolasCARPi/jquery_jeditable
t.create('Code quality')
- .get('/e27821fb6289410b8f58338c7e0bc686.json')
+ .get('/b6a59cdf5ca64eab9104928d4f9bbb97.json')
.expectBadge({
label: 'code quality',
message: codacyGrade,
})
t.create('Code quality on branch')
- .get('/e27821fb6289410b8f58338c7e0bc686/master.json')
+ .get('/b6a59cdf5ca64eab9104928d4f9bbb97/master.json')
.expectBadge({
label: 'code quality',
message: codacyGrade,
diff --git a/services/codacy/codacy-helpers.js b/services/codacy/codacy-helpers.js
index 308456981bd40..8408c492ad7e0 100644
--- a/services/codacy/codacy-helpers.js
+++ b/services/codacy/codacy-helpers.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
const codacyGrade = Joi.equal('A', 'B', 'C', 'D', 'E', 'F')
-module.exports = { codacyGrade }
+export { codacyGrade }
diff --git a/services/codeclimate/codeclimate-analysis-redirector.service.js b/services/codeclimate/codeclimate-analysis-redirector.service.js
deleted file mode 100644
index e70062db79129..0000000000000
--- a/services/codeclimate/codeclimate-analysis-redirector.service.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-module.exports = [
- // http://github.com/badges/shields/issues/1387
- // https://github.com/badges/shields/pull/3320#issuecomment-483795000
- redirector({
- name: 'CodeclimateCoverageMaintainabilityRedirect',
- category: 'analysis',
- route: {
- base: 'codeclimate/maintainability-letter',
- pattern: ':user/:repo',
- },
- transformPath: ({ user, repo }) =>
- `/codeclimate/maintainability/${user}/${repo}`,
- dateAdded: new Date('2019-04-16'),
- }),
-]
diff --git a/services/codeclimate/codeclimate-analysis-redirector.tester.js b/services/codeclimate/codeclimate-analysis-redirector.tester.js
deleted file mode 100644
index 2e6eb7e42c8fe..0000000000000
--- a/services/codeclimate/codeclimate-analysis-redirector.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'CodeclimateCoverageRedirector',
- title: 'Code Climate Coverage Redirector',
- pathPrefix: '/codeclimate',
-}))
-
-t.create('Maintainability letter alias')
- .get('/maintainability-letter/jekyll/jekyll.svg')
- .expectRedirect('/codeclimate/maintainability/jekyll/jekyll.svg')
diff --git a/services/codeclimate/codeclimate-analysis.service.js b/services/codeclimate/codeclimate-analysis.service.js
deleted file mode 100644
index 3468099657b75..0000000000000
--- a/services/codeclimate/codeclimate-analysis.service.js
+++ /dev/null
@@ -1,176 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale, letterScore } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
-const { keywords, isLetterGrade, fetchRepo } = require('./codeclimate-common')
-
-const schema = Joi.object({
- data: Joi.object({
- meta: Joi.object({
- issues_count: nonNegativeInteger,
- }).required(),
- attributes: Joi.object({
- ratings: Joi.array()
- .items(
- Joi.object({
- letter: isLetterGrade,
- measure: Joi.object({
- value: Joi.number().required(),
- }).required(),
- })
- )
- .length(1)
- .required(),
- }).required(),
- }).required(),
-}).required()
-
-const maintainabilityColorScale = colorScale(
- [50, 80, 90, 95],
- ['red', 'yellow', 'yellowgreen', 'green', 'brightgreen']
-)
-const techDebtColorScale = colorScale(
- [5, 10, 20, 50],
- ['brightgreen', 'green', 'yellowgreen', 'yellow', 'red']
-)
-const issueColorScale = colorScale(
- [1, 5, 10, 20],
- ['brightgreen', 'green', 'yellowgreen', 'yellow', 'red']
-)
-
-const variantMap = {
- maintainability: {
- transform: data => ({
- maintainabilityLetter: data.attributes.ratings[0].letter,
- }),
- render: ({ maintainabilityLetter }) => ({
- label: 'maintainability',
- message: maintainabilityLetter,
- color: letterScore(maintainabilityLetter),
- }),
- },
- 'maintainability-percentage': {
- transform: data => ({
- techDebtPercentage: data.attributes.ratings[0].measure.value,
- }),
- render: ({ techDebtPercentage }) => {
- // maintainability = 100 - technical debt.
- const maintainabilityPercentage = 100 - techDebtPercentage
- return {
- label: 'maintainability',
- message: `${maintainabilityPercentage.toFixed(0)}%`,
- color: maintainabilityColorScale(maintainabilityPercentage),
- }
- },
- },
- 'tech-debt': {
- transform: data => ({
- techDebtPercentage: data.attributes.ratings[0].measure.value,
- }),
- render: ({ techDebtPercentage }) => ({
- label: 'technical debt',
- message: `${techDebtPercentage.toFixed(0)}%`,
- color: techDebtColorScale(techDebtPercentage),
- }),
- },
- issues: {
- transform: data => ({
- issueCount: data.meta.issues_count,
- }),
- render: ({ issueCount }) => ({
- label: 'issues',
- message: `${issueCount}`,
- color: issueColorScale(issueCount),
- }),
- },
-}
-
-module.exports = class CodeclimateAnalysis extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'codeclimate',
- pattern:
- ':variant(maintainability|maintainability-percentage|tech-debt|issues)/:user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Code Climate maintainability',
- pattern:
- ':format(maintainability|maintainability-percentage)/:user/:repo',
- namedParams: {
- format: 'maintainability',
- user: 'angular',
- repo: 'angular',
- },
- staticPreview: this.render({
- variant: 'maintainability',
- maintainabilityLetter: 'F',
- }),
- keywords,
- },
- {
- title: 'Code Climate issues',
- pattern: 'issues/:user/:repo',
- namedParams: { user: 'twbs', repo: 'bootstrap' },
- staticPreview: this.render({
- variant: 'issues',
- issueCount: '89',
- }),
- keywords,
- },
- {
- title: 'Code Climate technical debt',
- pattern: 'tech-debt/:user/:repo',
- namedParams: { user: 'angular', repo: 'angular' },
- staticPreview: this.render({
- variant: 'tech-debt',
- techDebtPercentage: 3.0,
- }),
- keywords,
- },
- ]
- }
-
- static render({ variant, ...props }) {
- const { render } = variantMap[variant]
-
- return render(props)
- }
-
- async fetch({ user, repo }) {
- const {
- id: repoId,
- relationships: {
- latest_default_branch_snapshot: { data: snapshotInfo },
- },
- } = await fetchRepo(this, { user, repo })
- if (snapshotInfo === null) {
- throw new NotFound({ prettyMessage: 'snapshot not found' })
- }
- const { data } = await this._requestJson({
- schema,
- url: `https://api.codeclimate.com/v1/repos/${repoId}/snapshots/${snapshotInfo.id}`,
- })
- return data
- }
-
- async handle({ variant, user, repo }) {
- const { transform } = variantMap[variant]
-
- const data = await this.fetch({ user, repo })
- const props = transform(data)
- return this.constructor.render({
- variant,
- ...props,
- })
- }
-}
diff --git a/services/codeclimate/codeclimate-analysis.tester.js b/services/codeclimate/codeclimate-analysis.tester.js
deleted file mode 100644
index 52c8257c96900..0000000000000
--- a/services/codeclimate/codeclimate-analysis.tester.js
+++ /dev/null
@@ -1,79 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Examples for this service can be found through the explore page:
-// https://codeclimate.com/explore
-
-t.create('issues count')
- .get('/issues/angular/angular.json')
- .expectBadge({
- label: 'issues',
- message: Joi.number()
- .integer()
- .positive(),
- })
-
-t.create('technical debt percentage')
- .get('/tech-debt/angular/angular.json')
- .expectBadge({
- label: 'technical debt',
- message: isIntegerPercentage,
- })
-
-t.create('maintainability percentage')
- .get('/maintainability-percentage/angular/angular.json')
- .expectBadge({
- label: 'maintainability',
- message: isIntegerPercentage,
- })
-
-t.create('maintainability letter')
- .get('/maintainability/angular/angular.json')
- .expectBadge({
- label: 'maintainability',
- message: Joi.equal('A', 'B', 'C', 'D', 'E', 'F'),
- })
-
-t.create('maintainability letter for non-existent repo')
- .get('/maintainability/unknown/unknown.json')
- .expectBadge({
- label: 'analysis',
- message: 'repo not found',
- })
-
-t.create('maintainability letter for repo without snapshots')
- .get('/maintainability/kabisaict/flow.json')
- .expectBadge({
- label: 'analysis',
- message: 'snapshot not found',
- })
-
-t.create('malformed response for outer user repos query')
- .get('/maintainability/angular/angular.json')
- .intercept(nock =>
- nock('https://api.codeclimate.com')
- .get('/v1/repos?github_slug=angular%2Fangular')
- .reply(200, {
- data: [{}], // No relationships in the list of data elements.
- })
- )
- .expectBadge({
- label: 'analysis',
- message: 'invalid response data',
- })
-
-t.create('malformed response for inner specific repo query')
- .get('/maintainability/angular/angular.json')
- .intercept(nock =>
- nock('https://api.codeclimate.com', { allowUnmocked: true })
- .get(/\/v1\/repos\/[a-z0-9]+\/snapshots\/[a-z0-9]+/)
- .reply(200, {})
- ) // No data.
- .networkOn() // Combined with allowUnmocked: true, this allows the outer user repos query to go through.
- .expectBadge({
- label: 'analysis',
- message: 'invalid response data',
- })
diff --git a/services/codeclimate/codeclimate-common.js b/services/codeclimate/codeclimate-common.js
deleted file mode 100644
index fc5b09fca7e1a..0000000000000
--- a/services/codeclimate/codeclimate-common.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { NotFound } = require('..')
-
-const keywords = ['codeclimate']
-
-const isLetterGrade = Joi.equal('A', 'B', 'C', 'D', 'E', 'F').required()
-
-const repoSchema = Joi.object({
- data: Joi.array()
- .max(1)
- .items(
- Joi.object({
- id: Joi.string().required(),
- relationships: Joi.object({
- latest_default_branch_snapshot: Joi.object({
- data: Joi.object({
- id: Joi.string().required(),
- }).allow(null),
- }).required(),
- latest_default_branch_test_report: Joi.object({
- data: Joi.object({
- id: Joi.string().required(),
- }).allow(null),
- }).required(),
- }).required(),
- })
- )
- .required(),
-}).required()
-
-async function fetchRepo(serviceInstance, { user, repo }) {
- const {
- data: [repoInfo],
- } = await serviceInstance._requestJson({
- schema: repoSchema,
- url: 'https://api.codeclimate.com/v1/repos',
- options: { qs: { github_slug: `${user}/${repo}` } },
- })
- if (repoInfo === undefined) {
- throw new NotFound({ prettyMessage: 'repo not found' })
- }
- return repoInfo
-}
-
-module.exports = {
- keywords,
- isLetterGrade,
- fetchRepo,
-}
diff --git a/services/codeclimate/codeclimate-coverage-redirector.service.js b/services/codeclimate/codeclimate-coverage-redirector.service.js
deleted file mode 100644
index 57b440c855aaf..0000000000000
--- a/services/codeclimate/codeclimate-coverage-redirector.service.js
+++ /dev/null
@@ -1,39 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-module.exports = [
- // http://github.com/badges/shields/issues/1387
- // https://github.com/badges/shields/pull/3320#issuecomment-483795000
- redirector({
- name: 'CodeclimateCoveragePercentageRedirect',
- category: 'coverage',
- route: {
- base: 'codeclimate',
- pattern: ':alias(c|coverage-percentage)/:user/:repo',
- },
- transformPath: ({ user, repo }) => `/codeclimate/coverage/${user}/${repo}`,
- dateAdded: new Date('2019-04-15'),
- }),
- redirector({
- name: 'CodeclimateCoverageLetterRedirect',
- category: 'coverage',
- route: {
- base: 'codeclimate/c-letter',
- pattern: ':user/:repo',
- },
- transformPath: ({ user, repo }) =>
- `/codeclimate/coverage-letter/${user}/${repo}`,
- dateAdded: new Date('2019-04-15'),
- }),
- redirector({
- name: 'CodeclimateTopLevelCoverageRedirect',
- category: 'coverage',
- route: {
- base: 'codeclimate',
- pattern: ':user/:repo',
- },
- transformPath: ({ user, repo }) => `/codeclimate/coverage/${user}/${repo}`,
- dateAdded: new Date('2019-04-15'),
- }),
-]
diff --git a/services/codeclimate/codeclimate-coverage-redirector.tester.js b/services/codeclimate/codeclimate-coverage-redirector.tester.js
deleted file mode 100644
index 2ea8184631a30..0000000000000
--- a/services/codeclimate/codeclimate-coverage-redirector.tester.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'CodeclimateCoverageRedirector',
- title: 'Code Climate Coverage Redirector',
- pathPrefix: '/codeclimate',
-}))
-
-t.create('Top-level coverage shortcut')
- .get('/jekyll/jekyll.svg')
- .expectRedirect('/codeclimate/coverage/jekyll/jekyll.svg')
-
-t.create('Coverage shortcut')
- .get('/c/jekyll/jekyll.svg')
- .expectRedirect('/codeclimate/coverage/jekyll/jekyll.svg')
-
-t.create('Coverage letter shortcut')
- .get('/c-letter/jekyll/jekyll.svg')
- .expectRedirect('/codeclimate/coverage-letter/jekyll/jekyll.svg')
-
-t.create('Coverage percentage shortcut')
- .get('/coverage-percentage/jekyll/jekyll.svg')
- .expectRedirect('/codeclimate/coverage/jekyll/jekyll.svg')
diff --git a/services/codeclimate/codeclimate-coverage.service.js b/services/codeclimate/codeclimate-coverage.service.js
deleted file mode 100644
index a71ebd414ecd9..0000000000000
--- a/services/codeclimate/codeclimate-coverage.service.js
+++ /dev/null
@@ -1,91 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage, letterScore } = require('../color-formatters')
-const { BaseJsonService, NotFound } = require('..')
-const { keywords, isLetterGrade, fetchRepo } = require('./codeclimate-common')
-
-const schema = Joi.object({
- data: Joi.object({
- attributes: Joi.object({
- covered_percent: Joi.number().required(),
- rating: Joi.object({
- letter: isLetterGrade,
- }).required(),
- }).required(),
- }).allow(null),
-}).required()
-
-module.exports = class CodeclimateCoverage extends BaseJsonService {
- static get category() {
- return 'coverage'
- }
-
- static get route() {
- return {
- base: 'codeclimate',
- pattern: ':format(coverage|coverage-letter)/:user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Code Climate coverage',
- namedParams: { format: 'coverage', user: 'jekyll', repo: 'jekyll' },
- staticPreview: this.render({
- format: 'coverage',
- percentage: 95.123,
- letter: 'A',
- }),
- keywords,
- },
- ]
- }
-
- static render({ wantLetter, percentage, letter }) {
- if (wantLetter) {
- return {
- message: letter,
- color: letterScore(letter),
- }
- } else {
- return {
- message: `${percentage.toFixed(0)}%`,
- color: coveragePercentage(percentage),
- }
- }
- }
-
- async fetch({ user, repo }) {
- const {
- id: repoId,
- relationships: {
- latest_default_branch_test_report: { data: testReportInfo },
- },
- } = await fetchRepo(this, { user, repo })
- if (testReportInfo === null) {
- throw new NotFound({ prettyMessage: 'test report not found' })
- }
- const { data } = await this._requestJson({
- schema,
- url: `https://api.codeclimate.com/v1/repos/${repoId}/test_reports/${testReportInfo.id}`,
- })
- return data
- }
-
- async handle({ format, user, repo }) {
- const {
- attributes: {
- rating: { letter },
- covered_percent: percentage,
- },
- } = await this.fetch({ user, repo })
-
- return this.constructor.render({
- wantLetter: format === 'coverage-letter',
- letter,
- percentage,
- })
- }
-}
diff --git a/services/codeclimate/codeclimate-coverage.tester.js b/services/codeclimate/codeclimate-coverage.tester.js
deleted file mode 100644
index 02c89117d589d..0000000000000
--- a/services/codeclimate/codeclimate-coverage.tester.js
+++ /dev/null
@@ -1,36 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Examples for this service can be found through the explore page:
-// https://codeclimate.com/explore
-
-t.create('test coverage percentage')
- .get('/coverage/codeclimate/minidoc.json')
- .expectBadge({
- label: 'coverage',
- message: isIntegerPercentage,
- })
-
-t.create('test coverage letter')
- .get('/coverage-letter/codeclimate/minidoc.json')
- .expectBadge({
- label: 'coverage',
- message: Joi.equal('A', 'B', 'C', 'D', 'E', 'F'),
- })
-
-t.create('test coverage percentage for non-existent repo')
- .get('/coverage/unknown/unknown.json')
- .expectBadge({
- label: 'coverage',
- message: 'repo not found',
- })
-
-t.create('test coverage percentage for repo without test reports')
- .get('/coverage/angular/angular.js.json')
- .expectBadge({
- label: 'coverage',
- message: 'test report not found',
- })
diff --git a/services/codeclimate/codeclimate.service.js b/services/codeclimate/codeclimate.service.js
new file mode 100644
index 0000000000000..7d1730c98ae61
--- /dev/null
+++ b/services/codeclimate/codeclimate.service.js
@@ -0,0 +1,11 @@
+import { retiredService } from '../index.js'
+
+export const CodeClimate = retiredService({
+ category: 'analysis',
+ route: {
+ base: 'codeclimate',
+ pattern: ':various+',
+ },
+ label: 'codeclimate',
+ dateAdded: new Date('2025-11-02'),
+})
diff --git a/services/codeclimate/codeclimate.tester.js b/services/codeclimate/codeclimate.tester.js
new file mode 100644
index 0000000000000..b8f939f78d644
--- /dev/null
+++ b/services/codeclimate/codeclimate.tester.js
@@ -0,0 +1,14 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'codeclimate',
+ title: 'CodeClimate',
+ pathPrefix: '/codeclimate',
+})
+
+t.create('test coverage')
+ .get('/coverage/codeclimate/codeclimate.json')
+ .expectBadge({
+ label: 'codeclimate',
+ message: 'retired badge',
+ })
diff --git a/services/codecov/codecov-redirect.service.js b/services/codecov/codecov-redirect.service.js
index 9d56de406e9b0..ec79d1b0e586d 100644
--- a/services/codecov/codecov-redirect.service.js
+++ b/services/codecov/codecov-redirect.service.js
@@ -1,26 +1,15 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-const vcsSNameShortFormMap = {
- bb: 'bitbucket',
- gh: 'github',
- gl: 'gitlab',
-}
-
-module.exports = [
- redirector({
+export default [
+ retiredService({
category: 'coverage',
+ label: 'codecov',
route: {
base: 'codecov/c',
pattern:
'token/:token/:vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch*',
},
- transformPath: ({ vcsName, user, repo, branch }) => {
- const vcs = vcsSNameShortFormMap[vcsName] || vcsName
- return `/codecov/c/${vcs}/${user}/${repo}${branch ? `/${branch}` : ''}`
- },
- transformQueryParams: ({ token }) => ({ token }),
- dateAdded: new Date('2019-03-04'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
]
diff --git a/services/codecov/codecov-redirect.tester.js b/services/codecov/codecov-redirect.tester.js
index 1a9154db681c2..f50f6eabd361f 100644
--- a/services/codecov/codecov-redirect.tester.js
+++ b/services/codecov/codecov-redirect.tester.js
@@ -1,27 +1,28 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'CodecovTokenRedirect',
title: 'CodecovTokenRedirect',
pathPrefix: '/codecov',
-}))
+})
t.create('codecov token')
- .get('/c/token/abc123def456/gh/codecov/private-example.svg')
- .expectRedirect(
- '/codecov/c/github/codecov/private-example.svg?token=abc123def456'
- )
+ .get('/c/token/abc123def456/gh/codecov/private-example.json')
+ .expectBadge({
+ label: 'codecov',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('codecov branch token')
- .get('/c/token/abc123def456/bb/private-shields/private-badges/master.svg')
- .expectRedirect(
- '/codecov/c/bitbucket/private-shields/private-badges/master.svg?token=abc123def456'
- )
+ .get('/c/token/abc123def456/bb/private-shields/private-badges/master.json')
+ .expectBadge({
+ label: 'codecov',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('codecov gl short form expanded to long form')
- .get('/c/token/abc123def456/gl/private-shields/private-badges/master.svg')
- .expectRedirect(
- '/codecov/c/gitlab/private-shields/private-badges/master.svg?token=abc123def456'
- )
+ .get('/c/token/abc123def456/gl/private-shields/private-badges/master.json')
+ .expectBadge({
+ label: 'codecov',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/codecov/codecov.service.js b/services/codecov/codecov.service.js
index f69cfe76538dd..65fd9e1080854 100644
--- a/services/codecov/codecov.service.js
+++ b/services/codecov/codecov.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { BaseSvgScrapingService, pathParam, queryParam } from '../index.js'
+import { parseJson } from '../../core/base-service/json.js'
// https://docs.codecov.io/reference#totals
// A new repository that's been added but never had any coverage reports
@@ -10,7 +9,7 @@ const { BaseJsonService } = require('..')
// the `totals` object can also be missing for the latest commit.
// Accordingly the schema is a bit relaxed to support those scenarios
// and then they are handled in the transform and render functions.
-const schema = Joi.object({
+const legacySchema = Joi.object({
commit: Joi.object({
totals: Joi.object({
c: Joi.number().required(),
@@ -20,72 +19,119 @@ const schema = Joi.object({
const queryParamSchema = Joi.object({
token: Joi.string(),
+ // https://docs.codecov.io/docs/flags
+ // Flags Must consist only of alphanumeric characters, '_', '-', or '.'
+ // and not exceed 45 characters.
+ flag: Joi.string().regex(/^[\w.-]{1,45}$/),
+ // https://docs.codecov.com/docs/components
+ // There's no explicit documentation on the valid patterns for component
+ // names and IDs. The component name in particular seems fairly flexible.
+ component: Joi.string().regex(/^.+$/),
}).required()
-const documentation = `
-
- You may specify a Codecov token to get coverage for a private repository.
-
-
- See the Codecov Docs
- for more information about creating a token.
-
+const schema = Joi.object({
+ message: Joi.string()
+ .regex(/^\d{1,3}%|unknown$/)
+ .required(),
+})
+
+const svgValueMatcher = />(\d{1,3}%|unknown)<\/text><\/g>/
+
+const badgeTokenPattern = /^\w{10}$/
+
+const description = `
+You may specify a Codecov badge token to get coverage for a private repository.
+
+You can find the token under the badge section of your project settings page, in this url: https://codecov.io/[vcsName]/[user]/[repo]/config/badge.
`
-module.exports = class Codecov extends BaseJsonService {
- static get category() {
- return 'coverage'
- }
+const tokenDescription = `Required only for private repositories.`
- static get route() {
- return {
- base: 'codecov/c',
- // https://docs.codecov.io/docs#section-common-questions
- // Github, BitBucket, and GitLab are the only supported options (long or short form)
- pattern:
- ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch*',
- queryParamSchema,
- }
+const flagDescription = `
+Display coverage for a specific subset of your project.
+See [Codecov's documentation](https://docs.codecov.io/docs/flags) for details.
+`
+
+const componentDescription = `
+Display coverage for a specific component of your project.
+See [Codecov's documentation](https://docs.codecov.com/docs/components) for details.
+`
+
+export default class Codecov extends BaseSvgScrapingService {
+ static category = 'coverage'
+ static route = {
+ base: 'codecov/c',
+ // https://docs.codecov.io/docs#section-common-questions
+ // Github, BitBucket, and GitLab are the only supported options (long or short form)
+ pattern: ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Codecov',
- pattern: ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo',
- namedParams: {
- vcsName: 'github',
- user: 'codecov',
- repo: 'example-python',
- },
- queryParams: {
- token: 'abc123def456',
- },
- staticPreview: this.render({ coverage: 90 }),
- documentation,
+ static openApi = {
+ '/codecov/c/{vcsName}/{user}/{repo}': {
+ get: {
+ summary: 'Codecov',
+ description,
+ parameters: [
+ pathParam({
+ name: 'vcsName',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsName') },
+ }),
+ pathParam({ name: 'user', example: 'codecov' }),
+ pathParam({ name: 'repo', example: 'umbrella' }),
+ queryParam({
+ name: 'token',
+ description: tokenDescription,
+ example: 'a1b2c3d4e5',
+ }),
+ queryParam({
+ name: 'flag',
+ description: flagDescription,
+ example: 'flag_name',
+ }),
+ queryParam({
+ name: 'component',
+ description: componentDescription,
+ example: 'component_id_or_name',
+ }),
+ ],
},
- {
- title: 'Codecov branch',
- pattern:
- ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch',
- namedParams: {
- vcsName: 'github',
- user: 'codecov',
- repo: 'example-python',
- branch: 'master',
- },
- queryParams: {
- token: 'abc123def456',
- },
- staticPreview: this.render({ coverage: 90 }),
- documentation,
+ },
+ '/codecov/c/{vcsName}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Codecov (with branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'vcsName',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsName') },
+ }),
+ pathParam({ name: 'user', example: 'codecov' }),
+ pathParam({ name: 'repo', example: 'example-node' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'token',
+ description: tokenDescription,
+ example: 'a1b2c3d4e5',
+ }),
+ queryParam({
+ name: 'flag',
+ description: flagDescription,
+ example: 'flag_name',
+ }),
+ queryParam({
+ name: 'component',
+ description: componentDescription,
+ example: 'component_id_or_name',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'coverage' }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ coverage }) {
if (coverage === 'unknown') {
@@ -100,30 +146,31 @@ module.exports = class Codecov extends BaseJsonService {
}
}
- async fetch({ vcsName, user, repo, branch, token }) {
+ // Here for backward-compatibility purpose.
+ async legacyFetch({ vcsName, user, repo, branch, token }) {
// Codecov Docs: https://docs.codecov.io/reference#section-get-a-single-repository
- let url = `https://codecov.io/api/${vcsName}/${user}/${repo}`
- if (branch) {
- url += `/branches/${branch}`
- }
- const options = {}
- if (token) {
- options.headers = {
- Authorization: `token ${token}`,
- }
- }
- return this._requestJson({
- schema,
- options,
+ const url = `https://codecov.io/api/${vcsName}/${user}/${repo}${
+ branch ? `/branch/${branch}` : ''
+ }`
+ const { buffer } = await this._request({
url,
- errorMessages: {
+ options: {
+ headers: {
+ Accept: 'application/json',
+ Authorization: `token ${token}`,
+ },
+ },
+ httpErrors: {
401: 'not authorized to access repository',
404: 'repository not found',
},
})
+ const json = parseJson(buffer)
+ return this.constructor._validate(json, legacySchema)
}
- transform({ json }) {
+ // Here for backward-compatibility purpose.
+ legacyTransform({ json }) {
if (!json.commit || !json.commit.totals) {
return { coverage: 'unknown' }
}
@@ -131,9 +178,53 @@ module.exports = class Codecov extends BaseJsonService {
return { coverage: +json.commit.totals.c }
}
- async handle({ vcsName, user, repo, branch }, { token }) {
- const json = await this.fetch({ vcsName, user, repo, branch, token })
- const { coverage } = this.transform({ json })
+ // Doesn't support `flag` or `component` features. Here for backward-compatibility purpose.
+ async legacyHandle({ vcsName, user, repo, branch }, { token }) {
+ const json = await this.legacyFetch({ vcsName, user, repo, branch, token })
+ const { coverage } = this.legacyTransform({ json })
+ return this.constructor.render({ coverage })
+ }
+
+ async fetch({ vcsName, user, repo, branch, token, flag, component }) {
+ const url = `https://codecov.io/${vcsName}/${user}/${repo}${
+ branch ? `/branch/${branch}` : ''
+ }/graph/badge.svg`
+ return this._requestSvg({
+ schema,
+ valueMatcher: svgValueMatcher,
+ url,
+ options: {
+ searchParams: { token, flag, component },
+ },
+ httpErrors: token ? { 400: 'invalid token pattern' } : {},
+ })
+ }
+
+ transform({ data }) {
+ // data extracted from svg. e.g.: 42% / unknown
+ let coverage = data.message || 'unknown'
+ if (coverage.slice(-1) === '%') {
+ // remove the trailing %
+ coverage = Number(coverage.slice(0, -1))
+ }
+ return { coverage }
+ }
+
+ async handle({ vcsName, user, repo, branch }, { token, flag, component }) {
+ if (!flag && !component && token && !badgeTokenPattern.test(token)) {
+ return this.legacyHandle({ vcsName, user, repo, branch }, { token })
+ }
+
+ const data = await this.fetch({
+ vcsName,
+ user,
+ repo,
+ branch,
+ token,
+ flag,
+ component,
+ })
+ const { coverage } = this.transform({ data })
return this.constructor.render({ coverage })
}
}
diff --git a/services/codecov/codecov.spec.js b/services/codecov/codecov.spec.js
index 9a60f7b8bbb63..d0f599a3fa0f6 100644
--- a/services/codecov/codecov.spec.js
+++ b/services/codecov/codecov.spec.js
@@ -1,11 +1,15 @@
-'use strict'
+import { test, forCases, given } from 'sazerac'
+import Codecov from './codecov.service.js'
-const { test, forCases, given } = require('sazerac')
-const Codecov = require('./codecov.service')
+describe('Codecov', function () {
+ test(Codecov.prototype.legacyTransform, () => {
+ forCases([given({ json: {} }), given({ json: { commit: {} } })]).expect({
+ coverage: 'unknown',
+ })
+ })
-describe('Codecov', function() {
test(Codecov.prototype.transform, () => {
- forCases([given({ json: {} }), given({ json: { commit: {} } })]).expect({
+ forCases([given({ data: { message: 'unknown' } })]).expect({
coverage: 'unknown',
})
})
diff --git a/services/codecov/codecov.tester.js b/services/codecov/codecov.tester.js
index 85bcc253aef46..b8d8d0813fe2b 100644
--- a/services/codecov/codecov.tester.js
+++ b/services/codecov/codecov.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets coverage status')
.get('/github/codecov/example-python.json')
@@ -10,6 +9,20 @@ t.create('gets coverage status')
message: isIntegerPercentage,
})
+t.create('gets coverage status with flag')
+ .get('/github/codecov/umbrella.json?flag=workerunit')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('gets coverage status with component')
+ .get('/github/codecov/gazebo.json?component=dir_shared')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
t.create('gets coverage status for branch')
.get('/github/codecov/example-python/master.json')
.expectBadge({
@@ -17,33 +30,131 @@ t.create('gets coverage status for branch')
message: isIntegerPercentage,
})
+t.create('gets coverage status for branch with flag')
+ .get('/github/codecov/umbrella/main.json?flag=workerunit')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('gets coverage status for branch with component')
+ .get('/github/codecov/gazebo/main.json?component=dir_shared')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
t.create('handles unknown repository')
.get('/github/codecov2/fake-not-even-a-little-bit-real-python.json')
.expectBadge({
label: 'coverage',
- message: 'repository not found',
+ message: 'unknown',
+ })
+
+t.create('handles unknown repository with flag')
+ .get(
+ '/github/codecov2/fake-not-even-a-little-bit-real-node.json?flag=istanbul_mocha',
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: 'unknown',
+ })
+
+t.create('handles unknown repository with component')
+ .get(
+ '/github/codecov2/fake-not-even-a-little-bit-real-node.json?component=dir_shared',
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: 'unknown',
+ })
+
+t.create('gets coverage status for unknown flag')
+ .get('/github/codecov/umbrella.json?flag=unknown_flag')
+ .expectBadge({
+ label: 'coverage',
+ message: 'unknown',
+ })
+
+t.create('gets coverage status for unknown component')
+ .get('/github/codecov/umbrella.json?component=unknown_component')
+ .expectBadge({
+ label: 'coverage',
+ message: 'unknown',
})
// Using a mocked response here because we did not have a known
// private repository hooked up with Codecov that we could use.
-t.create('handles unauthorized error')
+t.create('handles unauthorized private repository')
.get('/github/codecov/private-example-python.json')
.intercept(nock =>
- nock('https://codecov.io/api')
+ nock('https://codecov.io')
+ .get('/github/codecov/private-example-python/graph/badge.svg')
+ .reply(200, 'unknown ', {
+ 'Content-Type': 'image/svg+xml',
+ }),
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: 'unknown',
+ })
+
+t.create('handles unauthorized error (with api token)')
+ .get('/github/codecov/private-example-python.json?token=a1b2c3d4e5f6g7h8')
+ .intercept(nock =>
+ nock('https://codecov.io/api', {
+ reqheaders: {
+ authorization: 'token a1b2c3d4e5f6g7h8', // lgtm [js/hardcoded-credentials]
+ },
+ })
.get('/github/codecov/private-example-python')
- .reply(401)
+ .reply(401),
)
.expectBadge({
label: 'coverage',
message: 'not authorized to access repository',
})
+t.create('handles unknown repository (with api token)')
+ .get(
+ '/github/codecov2/fake-not-even-a-little-bit-real-python.json?token=a1b2c3d4e5f6g7h8',
+ )
+ .intercept(nock =>
+ nock('https://codecov.io/api', {
+ reqheaders: {
+ authorization: 'token a1b2c3d4e5f6g7h8', // lgtm [js/hardcoded-credentials]
+ },
+ })
+ .get('/github/codecov2/fake-not-even-a-little-bit-real-python')
+ .reply(404),
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: 'repository not found',
+ })
+
t.create('gets coverage for private repository')
- .get('/github/codecov/private-example-python.json?token=abc123def456')
+ .get('/gh/codecov/private-example-python.json?token=a1b2c3d4e5')
+ .intercept(nock =>
+ nock('https://codecov.io')
+ .get(
+ '/gh/codecov/private-example-python/graph/badge.svg?token=a1b2c3d4e5',
+ )
+ .reply(200, '100% ', {
+ 'Content-Type': 'image/svg+xml',
+ }),
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: '100%',
+ })
+
+t.create('gets coverage for private repository (with api token)')
+ .get('/github/codecov/private-example-python.json?token=a1b2c3d4e5f6g7h8')
.intercept(nock =>
nock('https://codecov.io/api', {
reqheaders: {
- authorization: 'token abc123def456',
+ authorization: 'token a1b2c3d4e5f6g7h8', // lgtm [js/hardcoded-credentials]
},
})
.get('/github/codecov/private-example-python')
@@ -53,7 +164,7 @@ t.create('gets coverage for private repository')
c: 94.75,
},
},
- })
+ }),
)
.expectBadge({
label: 'coverage',
diff --git a/services/codefactor/codefactor-grade.service.js b/services/codefactor/codefactor-grade.service.js
index 707661fb1b485..26ed28208b7bc 100644
--- a/services/codefactor/codefactor-grade.service.js
+++ b/services/codefactor/codefactor-grade.service.js
@@ -1,45 +1,66 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseSvgScrapingService } = require('..')
-const { isValidGrade, gradeColor } = require('./codefactor-helpers')
+import Joi from 'joi'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
+import { isValidGrade, gradeColor } from './codefactor-helpers.js'
const schema = Joi.object({
message: isValidGrade,
}).required()
-module.exports = class CodeFactorGrade extends BaseSvgScrapingService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'codefactor/grade',
- pattern: ':vcsType(github|bitbucket)/:user/:repo/:branch*',
- }
+export default class CodeFactorGrade extends BaseSvgScrapingService {
+ static category = 'analysis'
+ static route = {
+ base: 'codefactor/grade',
+ pattern: ':vcsType(github|bitbucket)/:user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'CodeFactor Grade',
- namedParams: {
- vcsType: 'github',
- user: 'pallets',
- repo: 'flask',
- branch: 'master',
- },
- staticPreview: this.render({ grade: 'B+' }),
+ static openApi = {
+ '/codefactor/grade/{vcsType}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'CodeFactor Grade (with branch)',
+ parameters: pathParams(
+ {
+ name: 'vcsType',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ },
+ {
+ name: 'user',
+ example: 'microsoft',
+ },
+ {
+ name: 'repo',
+ example: 'powertoys',
+ },
+ {
+ name: 'branch',
+ example: 'main',
+ },
+ ),
},
- ]
+ },
+ '/codefactor/grade/{vcsType}/{user}/{repo}': {
+ get: {
+ summary: 'CodeFactor Grade',
+ parameters: pathParams(
+ {
+ name: 'vcsType',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ },
+ {
+ name: 'user',
+ example: 'microsoft',
+ },
+ {
+ name: 'repo',
+ example: 'powertoys',
+ },
+ ),
+ },
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'code quality',
- }
- }
+ static defaultBadgeData = { label: 'code quality' }
static render({ grade }) {
return {
@@ -51,9 +72,10 @@ module.exports = class CodeFactorGrade extends BaseSvgScrapingService {
async handle({ vcsType, user, repo, branch }) {
const { message } = await this._requestSvg({
schema,
- url: `https://codefactor.io/repository/${vcsType}/${user}/${repo}/badge/${branch ||
- ''}`,
- errorMessages: { 404: 'repo or branch not found' },
+ url: `https://codefactor.io/repository/${vcsType}/${user}/${repo}/badge/${
+ branch || ''
+ }`,
+ httpErrors: { 404: 'repo or branch not found' },
})
return this.constructor.render({ grade: message })
}
diff --git a/services/codefactor/codefactor-grade.spec.js b/services/codefactor/codefactor-grade.spec.js
index a35b08f61c5af..a4ba4d95e8928 100644
--- a/services/codefactor/codefactor-grade.spec.js
+++ b/services/codefactor/codefactor-grade.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import CodeFactorGrade from './codefactor-grade.service.js'
-const { test, given } = require('sazerac')
-const CodeFactorGrade = require('./codefactor-grade.service')
-
-describe('CodeFactorGrade', function() {
+describe('CodeFactorGrade', function () {
test(CodeFactorGrade.render, () => {
given({ grade: 'A' }).expect({
message: 'A',
diff --git a/services/codefactor/codefactor-grade.tester.js b/services/codefactor/codefactor-grade.tester.js
index fb52737615602..c5c410c8bf45e 100644
--- a/services/codefactor/codefactor-grade.tester.js
+++ b/services/codefactor/codefactor-grade.tester.js
@@ -1,17 +1,14 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isValidGrade } from './codefactor-helpers.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { isValidGrade } = require('./codefactor-helpers')
-
-t.create('Grade')
- .get('/github/google/guava.json')
- .expectBadge({
- label: 'code quality',
- message: isValidGrade,
- })
+t.create('Grade').get('/github/microsoft/powertoys.json').expectBadge({
+ label: 'code quality',
+ message: isValidGrade,
+})
t.create('Grade (branch)')
- .get('/github/pallets/flask/master.json')
+ .get('/github/microsoft/powertoys/main.json')
.expectBadge({
label: 'code quality',
message: isValidGrade,
diff --git a/services/codefactor/codefactor-helpers.js b/services/codefactor/codefactor-helpers.js
index 6b2749eb7688d..d110eabdfb591 100644
--- a/services/codefactor/codefactor-helpers.js
+++ b/services/codefactor/codefactor-helpers.js
@@ -1,6 +1,4 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
// https://support.codefactor.io/i14-glossary
// https://github.com/badges/shields/issues/4269
@@ -31,4 +29,4 @@ function gradeColor(grade) {
return color
}
-module.exports = { isValidGrade, gradeColor }
+export { isValidGrade, gradeColor }
diff --git a/services/coderabbit/coderabbit-pull-request.service.js b/services/coderabbit/coderabbit-pull-request.service.js
new file mode 100644
index 0000000000000..75a53e8db9252
--- /dev/null
+++ b/services/coderabbit/coderabbit-pull-request.service.js
@@ -0,0 +1,71 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+
+const schema = Joi.object({
+ reviews: Joi.number().required(),
+}).required()
+
+class CodeRabbitPullRequest extends BaseJsonService {
+ static category = 'analysis'
+ static route = {
+ base: 'coderabbit',
+ pattern: 'prs/:provider(github|bitbucket|gitlab)/:org/:repo',
+ }
+
+ static openApi = {
+ '/coderabbit/prs/{provider}/{org}/{repo}': {
+ get: {
+ summary: 'CodeRabbit Pull Request Reviews',
+ description:
+ 'This badge pulls the number of PRs reviewed by [CodeRabbit](https://coderabbit.ai), AI code review tool',
+ parameters: pathParams(
+ {
+ name: 'provider',
+ example: 'github',
+ description: 'Version Control Provider',
+ schema: { type: 'string', enum: this.getEnum('provider') },
+ },
+ {
+ name: 'org',
+ example: 'coderabbitai',
+ description: 'Organization or User name',
+ },
+ {
+ name: 'repo',
+ example: 'ast-grep-essentials',
+ description: 'Repository name',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'coderabbit reviews',
+ }
+
+ static render({ reviews }) {
+ return {
+ message: metric(reviews),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ provider, org, repo }) {
+ return this._requestJson({
+ schema,
+ url: `https://api.coderabbit.ai/stats/${provider}/${org}/${repo}`,
+ httpErrors: {
+ 400: 'provider or repo not found',
+ },
+ })
+ }
+
+ async handle({ provider, org, repo }) {
+ const data = await this.fetch({ provider, org, repo })
+ return this.constructor.render(data)
+ }
+}
+
+export default CodeRabbitPullRequest
diff --git a/services/coderabbit/coderabbit-pull-request.tester.js b/services/coderabbit/coderabbit-pull-request.tester.js
new file mode 100644
index 0000000000000..9fa96b594de81
--- /dev/null
+++ b/services/coderabbit/coderabbit-pull-request.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('live CodeRabbitPullRequest')
+ .get('/prs/github/coderabbitai/ast-grep-essentials.json')
+ .expectBadge({
+ label: 'coderabbit reviews',
+ message: isMetric,
+ })
+
+t.create('live CodeRabbitPullRequest nonexistent org')
+ .get('/prs/github/not-valid/not-found.json')
+ .expectBadge({
+ label: 'coderabbit reviews',
+ message: 'provider or repo not found',
+ })
+
+t.create('live CodeRabbitPullRequest invalid repo')
+ .get('/prs/github/coderabbitai/invalid-repo-name.json')
+ .expectBadge({
+ label: 'coderabbit reviews',
+ message: 'provider or repo not found',
+ })
diff --git a/services/codeship/codeship.service.js b/services/codeship/codeship.service.js
index c1c176cba19c3..ef20589f6af51 100644
--- a/services/codeship/codeship.service.js
+++ b/services/codeship/codeship.service.js
@@ -1,86 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService } = require('..')
-
-const schema = Joi.object({
- message: Joi.alternatives()
- .try(
- isBuildStatus,
- Joi.equal('project not found', 'branch not found', 'ignored', 'blocked')
- )
- .required(),
-}).required()
-
-const pendingStatus = 'pending'
-const notBuiltStatus = 'not built'
-
-const statusMap = {
- testing: pendingStatus,
- waiting: pendingStatus,
- initiated: pendingStatus,
- stopped: notBuiltStatus,
- ignored: notBuiltStatus,
- blocked: notBuiltStatus,
- infrastructure_failure: 'failed',
-}
-
-module.exports = class Codeship extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'codeship',
- pattern: ':projectId/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Codeship',
- pattern: ':projectId',
- namedParams: {
- projectId: 'd6c1ddd0-16a3-0132-5f85-2e35c05e22b1',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
- },
- {
- title: 'Codeship (branch)',
- pattern: ':projectId/:branch',
- namedParams: {
- projectId: '0bdb0440-3af5-0133-00ea-0ebda3a33bf6',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'build' }
- }
-
- static render({ status }) {
- status = statusMap[status] || status
- return renderBuildStatusBadge({ status })
- }
-
- async fetch({ projectId, branch }) {
- const url = `https://app.codeship.com/projects/${projectId}/status`
- return this._requestSvg({
- schema,
- url,
- options: { qs: { branch } },
- valueMatcher: /(?:[.\s\S]*)\/><\/g> {
- given({ status: 'testing' }).expect(pending)
- given({ status: 'waiting' }).expect(pending)
- given({ status: 'initiated' }).expect(pending)
- given({ status: 'stopped' }).expect(notBuilt)
- given({ status: 'ignored' }).expect(notBuilt)
- given({ status: 'blocked' }).expect(notBuilt)
- given({ status: 'infrastructure_failure' }).expect({
- message: 'failing',
- color: 'red',
- label: undefined,
- })
- })
-})
diff --git a/services/codeship/codeship.tester.js b/services/codeship/codeship.tester.js
index dce57d7ee4ce9..8f8024d534ad7 100644
--- a/services/codeship/codeship.tester.js
+++ b/services/codeship/codeship.tester.js
@@ -1,26 +1,10 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = new ServiceTester({
+ id: 'codeship',
+ title: 'Codeship',
+})
-t.create('codeship (valid, no branch)')
- .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
-
-t.create('codeship (valid, with branch)')
- .get('/0bdb0440-3af5-0133-00ea-0ebda3a33bf6/master.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
-
-t.create('codeship (repo not found)')
- .get('/not-a-repo.json')
- .expectBadge({ label: 'build', message: 'project not found' })
-
-t.create('codeship (branch not found)')
- .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/not-a-branch.json')
- .expectBadge({ label: 'build', message: 'branch not found' })
+t.create('codeship (retired badge)')
+ .get('/30419df0-80ff-0135-f7fb-06994b6b032d.json')
+ .expectBadge({ label: 'codeship', message: 'retired badge' })
diff --git a/services/codetally/codetally.service.js b/services/codetally/codetally.service.js
deleted file mode 100644
index 22f5698a33c1a..0000000000000
--- a/services/codetally/codetally.service.js
+++ /dev/null
@@ -1,74 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- currency_sign: Joi.string().required(),
- amount: Joi.number().required(),
- multiplier: Joi.string()
- .allow('')
- .required(),
- currency_abbreviation: Joi.string().required(),
-}).required()
-
-module.exports = class Codetally extends BaseJsonService {
- static get category() {
- return 'funding'
- }
-
- static get route() {
- return {
- base: 'codetally',
- pattern: ':owner/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Codetally',
- namedParams: {
- owner: 'triggerman722',
- repo: 'colorstrap',
- },
- staticPreview: this.render({
- currency: '$',
- amount: 4.68,
- multiplier: 'K',
- }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'codetally',
- color: '#2E8B57',
- }
- }
-
- static render({ currency, amount, multiplier }) {
- return {
- message: `${currency}${amount.toFixed(2)} ${multiplier}`,
- }
- }
-
- async handle({ owner, repo }) {
- const url = `http://www.codetally.com/formattedshield/${owner}/${repo}`
- const json = await this._requestJson({
- url,
- schema,
- errorMessages: {
- 404: 'repo not found',
- 503: 'repo not found',
- },
- })
-
- return this.constructor.render({
- currency: json.currency_sign,
- amount: json.amount,
- multiplier: json.multiplier,
- })
- }
-}
diff --git a/services/codetally/codetally.tester.js b/services/codetally/codetally.tester.js
deleted file mode 100644
index f8e1639bdef20..0000000000000
--- a/services/codetally/codetally.tester.js
+++ /dev/null
@@ -1,38 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-// This test will extract the currency value from the
-// string value response from the server.
-// \b = Asset position at word boundary
-// \d+ = match a digit [0-9]
-// credit for this regex goes to:
-// https://stackoverflow.com/questions/38074000/how-to-get-float-value-from-string-using-regex
-
-t.create('Codetally')
- .get('/triggerman722/colorstrap.json')
- .timeout(10000)
- .expectBadge({
- label: 'codetally',
- message: Joi.string().regex(/\b\d+(?:.\d+)?/),
- })
-
-t.create('Empty')
- .get('/triggerman722/colorstrap.json')
- .intercept(nock =>
- nock('http://www.codetally.com')
- .get('/formattedshield/triggerman722/colorstrap')
- .reply(200, {
- currency_sign: '$',
- amount: '0.00',
- multiplier: '',
- currency_abbreviation: 'CAD',
- })
- )
- .expectBadge({ label: 'codetally', message: '$0.00' })
-
-t.create('Non existent')
- .get('/not/real.json')
- .timeout(10000)
- .expectBadge({ label: 'codetally', message: 'repo not found' })
diff --git a/services/color-formatters.js b/services/color-formatters.js
index ce9fa6e78b76b..708c25a6d83bd 100644
--- a/services/color-formatters.js
+++ b/services/color-formatters.js
@@ -1,11 +1,18 @@
/**
* Commonly-used functions for determining the colour to use for a badge,
* including colours based off download count, version number, etc.
+ *
+ * @module
*/
-'use strict'
-const moment = require('moment')
+import { valid, explain } from '@renovatebot/pep440'
+/**
+ * Determines the color used for a badge based on version.
+ *
+ * @param {string|number} version Version used for determining badge color
+ * @returns {string} Badge color
+ */
function version(version) {
if (typeof version !== 'string' && typeof version !== 'number') {
throw new Error(`Can't generate a version color for ${version}`)
@@ -15,13 +22,45 @@ function version(version) {
if (first === 'v') {
first = version[1]
}
- if (first === '0' || /alpha|beta|snapshot|dev|pre/i.test(version)) {
+ if (
+ first === '0' ||
+ /alpha|beta|snapshot|dev|pre|rc|scm|cvs/i.test(version)
+ ) {
return 'orange'
} else {
return 'blue'
}
}
+/**
+ * Determines the color used for a badge based on PEP440 versioning.
+ *
+ * @param {string|number} version Version used for determining badge color
+ * @returns {string} Badge color
+ */
+function pep440VersionColor(version) {
+ if (!valid(version)) {
+ return 'lightgrey'
+ }
+ const parsedVersion = explain(version)
+ if (parsedVersion.is_prerelease || parsedVersion.public.startsWith('0.')) {
+ return 'orange'
+ }
+ return 'blue'
+}
+
+/**
+ * Determines the color used for a badge by comparing the value and floor count values.
+ * The color can vary from red to bright green depending on the range the value lies in.
+ * Decreasing the value will shift the color towards red.
+ * Increasing the value will shift the color towards bright green.
+ *
+ * @param {number} value Current value
+ * @param {number} yellow Yellow color threshold, should be greater than 0
+ * @param {number} yellowgreen Yellowgreen color threshold, should be greater than yellow
+ * @param {number} green Green color threshold, should be greater than yellowgreen
+ * @returns {string} Badge color
+ */
function floorCount(value, yellow, yellowgreen, green) {
if (value <= 0) {
return 'red'
@@ -36,14 +75,37 @@ function floorCount(value, yellow, yellowgreen, green) {
}
}
+/**
+ * Determines the color used for a badge by comparing the download count and floor values.
+ * The color varies from red to bright green as the download count increases.
+ *
+ * @param {number} downloads Download count
+ * @returns {string} Badge color
+ */
function downloadCount(downloads) {
return floorCount(downloads, 10, 100, 1000)
}
+/**
+ * Determines the color used for a badge by comparing percentage and floor values.
+ * The color varies from red to bright green as the percentage increases.
+ *
+ * @param {number} percentage Percentage value
+ * @returns {string} Badge color
+ */
function coveragePercentage(percentage) {
return floorCount(percentage, 80, 90, 100)
}
+/**
+ * Determines the color used for a badge by matching score with grade values.
+ * The color varies from bright green to red as the score decreases.
+ * The score can be one of the following grade value: ['A', 'B', 'C', 'D', 'E'].
+ * The color defaults to red if the score does not matches with any of the grade values.
+ *
+ * @param {string} score Score value
+ * @returns {string} Badge color
+ */
function letterScore(score) {
if (score === 'A') {
return 'brightgreen'
@@ -60,6 +122,18 @@ function letterScore(score) {
}
}
+/**
+ * Creates a callback function that determines badge color from the colors array.
+ * If the colors array is provided then for n steps, there should be n + 1 color.
+ * If the colors array is not provided then it is chosen from the default colors array
+ * according to the size of the steps array.
+ *
+ * @param {number[]} steps Steps array
+ * @param {string[]} colors Colors array. If provided, should be of length steps.length + 1
+ * @param {boolean} reversed If true then the colors array will be considered in reverse order
+ * @returns {function(number): string} Function that finds the step index by comparing value
+ * with steps array and returns color from colors array for the corresponding step index
+ */
function colorScale(steps, colors, reversed) {
if (steps === undefined) {
throw Error('When invoking colorScale, steps should be provided.')
@@ -83,7 +157,7 @@ function colorScale(steps, colors, reversed) {
if (steps.length !== colors.length - 1) {
throw Error(
- 'When colors are provided, there should be n + 1 colors for n steps.'
+ 'When colors are provided, there should be n + 1 colors for n steps.',
)
}
@@ -100,18 +174,12 @@ function colorScale(steps, colors, reversed) {
}
}
-function age(date) {
- const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, true)
- const daysElapsed = moment().diff(moment(date), 'days')
- return colorByAge(daysElapsed)
-}
-
-module.exports = {
- version,
- downloadCount,
+export {
+ colorScale,
coveragePercentage,
+ downloadCount,
floorCount,
letterScore,
- colorScale,
- age,
+ pep440VersionColor,
+ version,
}
diff --git a/services/color-formatters.spec.js b/services/color-formatters.spec.js
index af632b51b0be0..735e07c3398c9 100644
--- a/services/color-formatters.spec.js
+++ b/services/color-formatters.spec.js
@@ -1,16 +1,14 @@
-'use strict'
-
-const { test, given, forCases } = require('sazerac')
-const { expect } = require('chai')
-const {
- coveragePercentage,
+import { expect } from 'chai'
+import { forCases, given, test } from 'sazerac'
+import {
colorScale,
+ coveragePercentage,
letterScore,
- age,
+ pep440VersionColor,
version,
-} = require('./color-formatters')
+} from './color-formatters.js'
-describe('Color formatters', function() {
+describe('Color formatters', function () {
const byPercentage = colorScale([Number.EPSILON, 80, 90, 100])
test(byPercentage, () => {
@@ -27,12 +25,12 @@ describe('Color formatters', function() {
forCases(
[-1, 0, 0.5, 1, 50, 80, 85, 90, 100, 101].map(v =>
- given(v).expect(coveragePercentage(v))
- )
+ given(v).expect(coveragePercentage(v)),
+ ),
).should("return '%s', for parity with coveragePercentage()")
})
- context('when reversed', function() {
+ context('when reversed', function () {
test(colorScale([7, 30, 180, 365, 730], undefined, true), () => {
given(3).expect('brightgreen')
given(7).expect('green')
@@ -54,30 +52,6 @@ describe('Color formatters', function() {
given('Z').expect('red')
})
- const monthsAgo = months => {
- const result = new Date()
- // This looks wack but it works.
- result.setMonth(result.getMonth() - months)
- return result
- }
- test(age, () => {
- given(Date.now())
- .describe('when given the current timestamp')
- .expect('brightgreen')
- given(new Date())
- .describe('when given the current Date')
- .expect('brightgreen')
- given(new Date(2001, 1, 1))
- .describe('when given a Date many years ago')
- .expect('red')
- given(monthsAgo(2))
- .describe('when given a Date two months ago')
- .expect('yellowgreen')
- given(monthsAgo(15))
- .describe('when given a Date 15 months ago')
- .expect('orange')
- })
-
test(version, () => {
forCases([given('1.0'), given(9), given(1.0)]).expect('blue')
@@ -89,23 +63,67 @@ describe('Color formatters', function() {
given('6.0-SNAPSHOT'),
given('1.0.1-dev'),
given('2.1.6-prerelease'),
+ given('2.1.6-RC1'),
+ given('cvs-1'),
+ given('scm-2'),
]).expect('orange')
expect(() => version(null)).to.throw(
Error,
- "Can't generate a version color for null"
+ "Can't generate a version color for null",
)
expect(() => version(undefined)).to.throw(
Error,
- "Can't generate a version color for undefined"
+ "Can't generate a version color for undefined",
)
expect(() => version(true)).to.throw(
Error,
- "Can't generate a version color for true"
+ "Can't generate a version color for true",
)
expect(() => version({})).to.throw(
Error,
- "Can't generate a version color for [object Object]"
+ "Can't generate a version color for [object Object]",
)
})
+
+ test(pep440VersionColor, () => {
+ forCases([
+ given('1.0.1'),
+ given('v2.1.6'),
+ given('1.0.1+abcd'),
+ given('1.0'),
+ given('v1'),
+ given(9),
+ given(1.0),
+ ]).expect('blue')
+
+ forCases([
+ given('1.0.1-rc1'),
+ given('1.0.1rc1'),
+ given('1.0.0-Beta'),
+ given('1.0.0Beta'),
+ given('1.1.0-alpha'),
+ given('1.1.0alpha'),
+ given('1.0.1-dev'),
+ given('1.0.1dev'),
+ given('2.1.6-b1'),
+ given('2.1.6b1'),
+ given('0.1.0'),
+ given('v0.1.0'),
+ given('v2.1.6-b1'),
+ given('0.1.0+abcd'),
+ given('2.1.6-b1+abcd'),
+ given('0.0.0'),
+ given(0.1),
+ given('0.9'),
+ ]).expect('orange')
+
+ forCases([
+ given('6.0.0-SNAPSHOT'),
+ given('2.1.6-prerelease'),
+ given(true),
+ given(null),
+ given('cheese'),
+ ]).expect('lightgrey')
+ })
})
diff --git a/services/conan/conan-version-helpers.js b/services/conan/conan-version-helpers.js
new file mode 100644
index 0000000000000..7db5a9882481f
--- /dev/null
+++ b/services/conan/conan-version-helpers.js
@@ -0,0 +1,21 @@
+import * as yaml from 'js-yaml'
+import { NotFound, InvalidResponse } from '../index.js'
+import { latest } from '../version.js'
+
+export function parseLatestVersionFromConfig(configYaml) {
+ let versions
+ try {
+ const config = yaml.load(configYaml)
+ versions = Object.keys(config.versions)
+ } catch (err) {
+ throw new InvalidResponse({
+ prettyMessage: 'invalid config.yml',
+ underlyingError: err,
+ })
+ }
+ const version = latest(versions)
+ if (version == null) {
+ throw new NotFound({ prettyMessage: 'no versions found' })
+ }
+ return version
+}
diff --git a/services/conan/conan-version-helpers.spec.js b/services/conan/conan-version-helpers.spec.js
new file mode 100644
index 0000000000000..49f20acbc2348
--- /dev/null
+++ b/services/conan/conan-version-helpers.spec.js
@@ -0,0 +1,33 @@
+import { expect } from 'chai'
+import { NotFound, InvalidResponse } from '../index.js'
+import { parseLatestVersionFromConfig } from './conan-version-helpers.js'
+
+describe('parseLatestVersionFromConfig', function () {
+ it('returns latest available version', function () {
+ expect(
+ parseLatestVersionFromConfig(`
+ versions:
+ 1.68.0:
+ folder: all
+ 1.70.0:
+ folder: all
+ 1.69.0:
+ folder: all
+ `),
+ ).to.equal('1.70.0')
+ })
+
+ it('rejects invalid yaml', function () {
+ expect(() => parseLatestVersionFromConfig('[')).to.throw(InvalidResponse)
+ })
+ it('treats no results array as invalid', function () {
+ expect(() =>
+ parseLatestVersionFromConfig('somethingElse: whatever'),
+ ).to.throw(InvalidResponse)
+ })
+ it('treats empty results array as not found', function () {
+ expect(() => parseLatestVersionFromConfig('versions: []')).to.throw(
+ NotFound,
+ )
+ })
+})
diff --git a/services/conan/conan-version.service.js b/services/conan/conan-version.service.js
new file mode 100644
index 0000000000000..2b5acef3406d2
--- /dev/null
+++ b/services/conan/conan-version.service.js
@@ -0,0 +1,39 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
+import { fetchRepoContent } from '../github/github-common-fetch.js'
+import { parseLatestVersionFromConfig } from './conan-version-helpers.js'
+
+export default class ConanVersion extends ConditionalGithubAuthV3Service {
+ static category = 'version'
+
+ static route = { base: 'conan/v', pattern: ':packageName' }
+
+ static openApi = {
+ '/conan/v/{packageName}': {
+ get: {
+ summary: 'Conan Center',
+ description: '[Conan](https://conan.io/) is a package manager for C++',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'boost',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'conan' }
+
+ async handle({ packageName }) {
+ const configContent = await fetchRepoContent(this, {
+ user: 'conan-io',
+ repo: 'conan-center-index',
+ branch: 'master',
+ filename: `recipes/${packageName}/config.yml`,
+ })
+
+ const version = parseLatestVersionFromConfig(configContent)
+
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/conan/conan-version.tester.js b/services/conan/conan-version.tester.js
new file mode 100644
index 0000000000000..75087c2d67d03
--- /dev/null
+++ b/services/conan/conan-version.tester.js
@@ -0,0 +1,17 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('gets the package version of zeromq')
+ .get('/zeromq.json')
+ .expectBadge({ label: 'conan', message: isSemver })
+
+t.create('returns not found for invalid package')
+ .get('/this package does not exist - shields test.json')
+ .expectBadge({
+ label: 'conan',
+ color: 'red',
+ message:
+ 'repo not found, branch not found, or recipes/this package does not exist - shields test/config.yml missing',
+ })
diff --git a/services/conda/conda-base.js b/services/conda/conda-base.js
index d2f2ec0c85d72..191562bd4b71a 100644
--- a/services/conda/conda-base.js
+++ b/services/conda/conda-base.js
@@ -1,32 +1,26 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
const condaSchema = Joi.object({
latest_version: Joi.string().required(),
- conda_platforms: Joi.array()
- .items(Joi.string())
- .required(),
+ conda_platforms: Joi.array().items(Joi.string()).required(),
files: Joi.array()
.items(
Joi.object({
ndownloads: nonNegativeInteger,
- })
+ }),
)
.required(),
}).required()
-module.exports = class BaseCondaService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'conda' }
- }
+export default class BaseCondaService extends BaseJsonService {
+ static defaultBadgeData = { label: 'conda' }
- async fetch({ channel, pkg }) {
+ async fetch({ channel, packageName }) {
return this._requestJson({
schema: condaSchema,
- url: `https://api.anaconda.org/package/${channel}/${pkg}`,
+ url: `https://api.anaconda.org/package/${channel}/${packageName}`,
})
}
}
diff --git a/services/conda/conda-downloads.service.js b/services/conda/conda-downloads.service.js
index 892354d6d74f2..afc5af72a96c8 100644
--- a/services/conda/conda-downloads.service.js
+++ b/services/conda/conda-downloads.service.js
@@ -1,45 +1,47 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import BaseCondaService from './conda-base.js'
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const BaseCondaService = require('./conda-base')
-
-module.exports = class CondaDownloads extends BaseCondaService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'conda',
- pattern: ':variant(d|dn)/:channel/:pkg',
- }
+export default class CondaDownloads extends BaseCondaService {
+ static category = 'downloads'
+ static route = {
+ base: 'conda',
+ pattern: ':variant(d|dn)/:channel/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Conda',
- namedParams: { channel: 'conda-forge', package: 'python' },
- pattern: 'dn/:channel/:package',
- staticPreview: this.render({ variant: 'dn', downloads: 5000000 }),
+ static openApi = {
+ '/conda/{variant}/{channel}/{packageName}': {
+ get: {
+ summary: 'Conda Downloads',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'dn',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'channel',
+ example: 'conda-forge',
+ },
+ {
+ name: 'packageName',
+ example: 'python',
+ },
+ ),
},
- ]
+ },
}
static render({ variant, downloads }) {
- return {
- label: variant === 'dn' ? 'downloads' : 'conda|downloads',
- message: metric(downloads),
- color: downloadCount(downloads),
- }
+ const labelOverride = variant === 'dn' ? 'downloads' : 'conda|downloads'
+ return renderDownloadsBadge({ downloads, labelOverride })
}
- async handle({ variant, channel, pkg }) {
- const json = await this.fetch({ channel, pkg })
+ async handle({ variant, channel, packageName }) {
+ const json = await this.fetch({ channel, packageName })
const downloads = json.files.reduce(
(total, file) => total + file.ndownloads,
- 0
+ 0,
)
return this.constructor.render({ variant, downloads })
}
diff --git a/services/conda/conda-downloads.tester.js b/services/conda/conda-downloads.tester.js
index 3d89629668df6..796cb38551b4d 100644
--- a/services/conda/conda-downloads.tester.js
+++ b/services/conda/conda-downloads.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('downloads')
- .get('/d/conda-forge/zlib.json')
- .expectBadge({
- label: 'conda|downloads',
- message: isMetric,
- })
+t.create('downloads').get('/d/conda-forge/zlib.json').expectBadge({
+ label: 'conda|downloads',
+ message: isMetric,
+})
t.create('downloads (skip prefix)')
.get('/dn/conda-forge/zlib.json')
diff --git a/services/conda/conda-license.service.js b/services/conda/conda-license.service.js
index 0e8c0278e7194..f0ee9e7953bb4 100644
--- a/services/conda/conda-license.service.js
+++ b/services/conda/conda-license.service.js
@@ -1,56 +1,45 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const toArray = require('../../core/base-service/to-array')
-const BaseCondaService = require('./conda-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import toArray from '../../core/base-service/to-array.js'
+import BaseCondaService from './conda-base.js'
const schema = Joi.object({
license: Joi.string().required(),
}).required()
-module.exports = class CondaLicense extends BaseCondaService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'conda',
- pattern: 'l/:channel/:pkg',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Conda - License',
- pattern: 'l/:channel/:package',
- namedParams: {
- channel: 'conda-forge',
- package: 'setuptools',
- },
- staticPreview: this.render({
- variant: 'l',
- channel: 'conda-forge',
- licenses: ['MIT'],
- }),
+export default class CondaLicense extends BaseCondaService {
+ static category = 'license'
+ static route = { base: 'conda', pattern: 'l/:channel/:packageName' }
+
+ static openApi = {
+ '/conda/l/{channel}/{packageName}': {
+ get: {
+ summary: 'Conda - License',
+ parameters: pathParams(
+ {
+ name: 'channel',
+ example: 'conda-forge',
+ },
+ {
+ name: 'packageName',
+ example: 'mlforecast',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ licenses }) {
return renderLicenseBadge({ licenses })
}
- async handle({ channel, pkg }) {
+ async handle({ channel, packageName }) {
const json = await this._requestJson({
schema,
- url: `https://api.anaconda.org/package/${channel}/${pkg}`,
+ url: `https://api.anaconda.org/package/${channel}/${packageName}`,
})
return this.constructor.render({
licenses: toArray(json.license),
diff --git a/services/conda/conda-license.tester.js b/services/conda/conda-license.tester.js
index 7cffa6ca27f39..25bf083ac698a 100644
--- a/services/conda/conda-license.tester.js
+++ b/services/conda/conda-license.tester.js
@@ -1,10 +1,9 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license')
- .get('/l/conda-forge/setuptools.json')
- .expectBadge({ label: 'license', message: 'MIT', color: 'green' })
+ .get('/l/conda-forge/mlforecast.json')
+ .expectBadge({ label: 'license', message: 'Apache-2.0', color: 'green' })
t.create('license (invalid)')
.get('/l/conda-forge/some-bogus-package-that-never-exists.json')
diff --git a/services/conda/conda-platform.service.js b/services/conda/conda-platform.service.js
index 90a38c6991c45..72a20de8c0497 100644
--- a/services/conda/conda-platform.service.js
+++ b/services/conda/conda-platform.service.js
@@ -1,31 +1,34 @@
-'use strict'
+import { pathParams } from '../index.js'
+import BaseCondaService from './conda-base.js'
-const BaseCondaService = require('./conda-base')
-
-module.exports = class CondaPlatform extends BaseCondaService {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return {
- base: 'conda',
- pattern: ':variant(p|pn)/:channel/:pkg',
- }
+export default class CondaPlatform extends BaseCondaService {
+ static category = 'platform-support'
+ static route = {
+ base: 'conda',
+ pattern: ':variant(p|pn)/:channel/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Conda',
- namedParams: { channel: 'conda-forge', package: 'python' },
- pattern: 'pn/:channel/:package',
- staticPreview: this.render({
- variant: 'pn',
- platforms: ['linux-64', 'win-32', 'osx-64', 'win-64'],
- }),
+ static openApi = {
+ '/conda/{variant}/{channel}/{packageName}': {
+ get: {
+ summary: 'Conda Platform',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'pn',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'channel',
+ example: 'conda-forge',
+ },
+ {
+ name: 'packageName',
+ example: 'python',
+ },
+ ),
},
- ]
+ },
}
static render({ variant, platforms }) {
@@ -35,8 +38,8 @@ module.exports = class CondaPlatform extends BaseCondaService {
}
}
- async handle({ variant, channel, pkg }) {
- const json = await this.fetch({ channel, pkg })
+ async handle({ variant, channel, packageName }) {
+ const json = await this.fetch({ channel, packageName })
return this.constructor.render({ variant, platforms: json.conda_platforms })
}
}
diff --git a/services/conda/conda-platform.tester.js b/services/conda/conda-platform.tester.js
index de7bfd6acf819..31545824142ad 100644
--- a/services/conda/conda-platform.tester.js
+++ b/services/conda/conda-platform.tester.js
@@ -1,15 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
const isCondaPlatform = Joi.string().regex(/^\w+-[\w\d]+( \| \w+-[\w\d]+)*$/)
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = await createServiceTester()
-t.create('platform')
- .get('/p/conda-forge/zlib.json')
- .expectBadge({
- label: 'conda|platform',
- message: isCondaPlatform,
- })
+t.create('platform').get('/p/conda-forge/zlib.json').expectBadge({
+ label: 'conda|platform',
+ message: isCondaPlatform,
+})
t.create('platform (skip prefix)')
.get('/pn/conda-forge/zlib.json')
diff --git a/services/conda/conda-version.service.js b/services/conda/conda-version.service.js
index acb763727877c..fa915f94b7610 100644
--- a/services/conda/conda-version.service.js
+++ b/services/conda/conda-version.service.js
@@ -1,60 +1,43 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import BaseCondaService from './conda-base.js'
-const { addv: versionText } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const BaseCondaService = require('./conda-base')
-
-module.exports = class CondaVersion extends BaseCondaService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'conda',
- pattern: ':variant(v|vn)/:channel/:pkg',
- }
+export default class CondaVersion extends BaseCondaService {
+ static category = 'version'
+ static route = {
+ base: 'conda',
+ pattern: ':variant(v|vn)/:channel/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Conda',
- namedParams: { channel: 'conda-forge', package: 'python' },
- pattern: 'v/:channel/:package',
- staticPreview: this.render({
- variant: 'v',
- channel: 'conda-forge',
- version: '3.7.1',
- }),
+ static openApi = {
+ '/conda/{variant}/{channel}/{packageName}': {
+ get: {
+ summary: 'Conda Version',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'vn',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'channel',
+ example: 'conda-forge',
+ },
+ {
+ name: 'packageName',
+ example: 'python',
+ },
+ ),
},
- {
- title: 'Conda (channel only)',
- namedParams: { channel: 'conda-forge', package: 'python' },
- pattern: 'vn/:channel/:package',
- staticPreview: this.render({
- variant: 'vn',
- channel: 'conda-forge',
- version: '3.7.1',
- }),
- },
- ]
- }
-
- static render({ variant, channel, version }) {
- return {
- label: variant === 'vn' ? channel : `conda|${channel}`,
- message: versionText(version),
- color: versionColor(version),
- }
+ },
}
- async handle({ variant, channel, pkg }) {
- const json = await this.fetch({ channel, pkg })
- return this.constructor.render({
- variant,
- channel,
+ async handle({ variant, channel, packageName }) {
+ const json = await this.fetch({ channel, packageName })
+ const defaultLabel = variant === 'vn' ? channel : `conda | ${channel}`
+ return renderVersionBadge({
version: json.latest_version,
+ defaultLabel,
})
}
}
diff --git a/services/conda/conda-version.tester.js b/services/conda/conda-version.tester.js
index 0bd049c80d011..e1e644c3b53f5 100644
--- a/services/conda/conda-version.tester.js
+++ b/services/conda/conda-version.tester.js
@@ -1,18 +1,13 @@
-'use strict'
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('version').get('/v/conda-forge/zlib.json').expectBadge({
+ label: 'conda | conda-forge',
+ message: isVPlusTripleDottedVersion,
+})
-t.create('version')
- .get('/v/conda-forge/zlib.json')
- .expectBadge({
- label: 'conda|conda-forge',
- message: isVPlusTripleDottedVersion,
- })
-
-t.create('version (skip prefix)')
- .get('/vn/conda-forge/zlib.json')
- .expectBadge({
- label: 'conda-forge',
- message: isVPlusTripleDottedVersion,
- })
+t.create('version (skip prefix)').get('/vn/conda-forge/zlib.json').expectBadge({
+ label: 'conda-forge',
+ message: isVPlusTripleDottedVersion,
+})
diff --git a/services/continuousphp/continuousphp.service.js b/services/continuousphp/continuousphp.service.js
deleted file mode 100644
index 69f3bf460ca8d..0000000000000
--- a/services/continuousphp/continuousphp.service.js
+++ /dev/null
@@ -1,83 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- status: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
-}).required()
-
-const statusMap = {
- unstable: 'yellow',
- running: 'blue',
-}
-
-module.exports = class ContinuousPhp extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'continuousphp',
- pattern: ':provider/:user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'continuousphp',
- pattern: ':provider/:user/:repo',
- namedParams: {
- provider: 'git-hub',
- user: 'doctrine',
- repo: 'dbal',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
- },
- {
- title: 'continuousphp',
- pattern: ':provider/:user/:repo/:branch',
- namedParams: {
- provider: 'git-hub',
- user: 'doctrine',
- repo: 'dbal',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'continuousphp' }
- }
-
- static render({ status }) {
- const badge = renderBuildStatusBadge({ label: 'build', status })
- const customColor = statusMap[status]
- if (customColor) {
- badge.color = customColor
- }
- return badge
- }
-
- async fetch({ provider, user, repo, branch }) {
- const url = `https://status.continuousphp.com/${provider}/${user}/${repo}/status-info`
- return this._requestJson({
- schema,
- url,
- options: { qs: { branch } },
- errorMessages: {
- 404: 'project not found',
- },
- })
- }
-
- async handle({ provider, user, repo, branch }) {
- const json = await this.fetch({ provider, user, repo, branch })
- return this.constructor.render({ status: json.status })
- }
-}
diff --git a/services/continuousphp/continuousphp.spec.js b/services/continuousphp/continuousphp.spec.js
deleted file mode 100644
index be92335ffab85..0000000000000
--- a/services/continuousphp/continuousphp.spec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const ContinuousPhp = require('./continuousphp.service')
-
-describe('ContinuousPhp', function() {
- test(ContinuousPhp.render, () => {
- given({ status: 'unstable' }).expect({
- label: 'build',
- message: 'unstable',
- color: 'yellow',
- })
- given({ status: 'running' }).expect({
- label: 'build',
- message: 'running',
- color: 'blue',
- })
- })
-})
diff --git a/services/continuousphp/continuousphp.tester.js b/services/continuousphp/continuousphp.tester.js
deleted file mode 100644
index 7f597be6b53c9..0000000000000
--- a/services/continuousphp/continuousphp.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('build status on default branch')
- .get('/git-hub/doctrine/dbal.json')
- .expectBadge({
- label: 'build',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
-
-t.create('build status on named branch')
- .get('/git-hub/doctrine/dbal/develop.json')
- .expectBadge({
- label: 'build',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
-
-t.create('unknown repo')
- .get('/git-hub/this-repo/does-not-exist.json')
- .expectBadge({ label: 'continuousphp', message: 'project not found' })
diff --git a/services/contributor-count.js b/services/contributor-count.js
index 523a5ace78235..373ba393fc54f 100644
--- a/services/contributor-count.js
+++ b/services/contributor-count.js
@@ -1,7 +1,18 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to contributor count.
+ *
+ * @module
+ */
-const { metric } = require('./text-formatters')
+import { metric } from './text-formatters.js'
+/**
+ * Determines the color used for a badge based on the contributor count.
+ * The color varies from red to bright green as the contributor count increases.
+ *
+ * @param {number} contributorCount Contributor count
+ * @returns {string} Badge color
+ */
function contributorColor(contributorCount) {
if (contributorCount > 2) {
return 'brightgreen'
@@ -12,6 +23,15 @@ function contributorColor(contributorCount) {
}
}
+/**
+ * Handles rendering concerns of badges that display contributor count.
+ * Determines the message and color of the badge according to the contributor count.
+ *
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} [attrs.label] If provided then badge label is set to this value
+ * @param {number} attrs.contributorCount Contributor count
+ * @returns {object} Badge with label, message and color properties
+ */
function renderContributorBadge({ label, contributorCount }) {
return {
label,
@@ -20,7 +40,4 @@ function renderContributorBadge({ label, contributorCount }) {
}
}
-module.exports = {
- contributorColor,
- renderContributorBadge,
-}
+export { contributorColor, renderContributorBadge }
diff --git a/services/contributor-count.spec.js b/services/contributor-count.spec.js
index 25c266287ef24..ba4e0fe6cbfb8 100644
--- a/services/contributor-count.spec.js
+++ b/services/contributor-count.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { renderContributorBadge } from './contributor-count.js'
-const { test, given } = require('sazerac')
-const { renderContributorBadge } = require('./contributor-count')
-
-describe('Contributor count helpers', function() {
+describe('Contributor count helpers', function () {
test(renderContributorBadge, () => {
given({ label: 'maintainers', contributorCount: 1 }).expect({
label: 'maintainers',
diff --git a/services/cookbook/cookbook.service.js b/services/cookbook/cookbook.service.js
index 82f908abefd93..cc8117ea96b66 100644
--- a/services/cookbook/cookbook.service.js
+++ b/services/cookbook/cookbook.service.js
@@ -1,39 +1,29 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({ version: Joi.string().required() }).required()
-module.exports = class Cookbook extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'cookbook/v',
- pattern: ':cookbook',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Chef cookbook',
- namedParams: { cookbook: 'chef-sugar' },
- staticPreview: renderVersionBadge({ version: '5.0.0' }),
+export default class Cookbook extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'cookbook/v', pattern: ':cookbook' }
+
+ static openApi = {
+ '/cookbook/v/{cookbook}': {
+ get: {
+ summary: 'Chef cookbook',
+ parameters: pathParams({
+ name: 'cookbook',
+ example: 'chef-sugar',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'cookbook' }
- }
+ static defaultBadgeData = { label: 'cookbook' }
async fetch({ cookbook }) {
- const url = `https://supermarket.getchef.com/api/v1/cookbooks/${cookbook}/versions/latest`
+ const url = `https://supermarket.chef.io/api/v1/cookbooks/${cookbook}/versions/latest`
return this._requestJson({ schema, url })
}
diff --git a/services/cookbook/cookbook.tester.js b/services/cookbook/cookbook.tester.js
index 9290be89c87f9..73fd4759392f8 100644
--- a/services/cookbook/cookbook.tester.js
+++ b/services/cookbook/cookbook.tester.js
@@ -1,23 +1,20 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('version')
- .get('/chef-sugar.json')
- .expectBadge({
- label: 'cookbook',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('version').get('/chef-sugar.json').expectBadge({
+ label: 'cookbook',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('version')
.get('/chef-sugar.json')
.intercept(nock =>
- nock('https://supermarket.getchef.com')
+ nock('https://supermarket.chef.io')
.get('/api/v1/cookbooks/chef-sugar/versions/latest')
.reply(200, {
version: '4.1.0',
- })
+ }),
)
.expectBadge({
label: 'cookbook',
diff --git a/services/coveralls/coveralls-redirector.service.js b/services/coveralls/coveralls-redirector.service.js
new file mode 100644
index 0000000000000..f77e9578e23cf
--- /dev/null
+++ b/services/coveralls/coveralls-redirector.service.js
@@ -0,0 +1,50 @@
+import { redirector } from '../index.js'
+
+export default [
+ redirector({
+ name: 'CoverallsGitHubRedirectWithBranch',
+ category: 'coverage',
+ route: {
+ base: 'coveralls',
+ pattern: ':user((?!github|bitbucket).*)/:repo/:branch+',
+ },
+ transformPath: ({ user, repo }) =>
+ `/coverallsCoverage/github/${user}/${repo}`,
+ transformQueryParams: ({ branch }) => ({ branch }),
+ dateAdded: new Date('2022-11-10'),
+ }),
+ redirector({
+ name: 'CoverallsGitHubRedirectWithoutBranch',
+ category: 'coverage',
+ route: {
+ base: 'coveralls',
+ pattern: ':user((?!github|bitbucket).*)/:repo',
+ },
+ transformPath: ({ user, repo }) =>
+ `/coverallsCoverage/github/${user}/${repo}`,
+ dateAdded: new Date('2021-02-23'),
+ }),
+ redirector({
+ name: 'CoverallsPreGitlabRedirectWithBranch',
+ category: 'coverage',
+ route: {
+ base: 'coveralls',
+ pattern: ':vcsType(github|bitbucket)/:user/:repo/:branch+',
+ },
+ transformPath: ({ vcsType, user, repo }) =>
+ `/coverallsCoverage/${vcsType}/${user}/${repo}`,
+ transformQueryParams: ({ branch }) => ({ branch }),
+ dateAdded: new Date('2022-11-10'),
+ }),
+ redirector({
+ name: 'CoverallsPreGitlabRedirectWithoutBranch',
+ category: 'coverage',
+ route: {
+ base: 'coveralls',
+ pattern: ':vcsType(github|bitbucket)/:user/:repo',
+ },
+ transformPath: ({ vcsType, user, repo }) =>
+ `/coverallsCoverage/${vcsType}/${user}/${repo}`,
+ dateAdded: new Date('2022-11-20'),
+ }),
+]
diff --git a/services/coveralls/coveralls-redirector.tester.js b/services/coveralls/coveralls-redirector.tester.js
new file mode 100644
index 0000000000000..000102a6b5e8b
--- /dev/null
+++ b/services/coveralls/coveralls-redirector.tester.js
@@ -0,0 +1,41 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'CoverallsTestsRedirector',
+ title: 'CoverallsTestsRedirector',
+ pathPrefix: '/coveralls',
+})
+
+t.create('Coveralls VCS type missing')
+ .get('/lemurheavy/coveralls-ruby.svg')
+ .expectRedirect('/coverallsCoverage/github/lemurheavy/coveralls-ruby.svg')
+
+t.create('Coveralls VCS type missing + specified branch')
+ .get('/jekyll/jekyll/master.svg')
+ .expectRedirect('/coverallsCoverage/github/jekyll/jekyll.svg?branch=master')
+
+t.create(
+ 'Redirect from before branch was a query param - github, with specified branch',
+)
+ .get('/github/jekyll/jekyll/master.svg')
+ .expectRedirect('/coverallsCoverage/github/jekyll/jekyll.svg?branch=master')
+
+t.create(
+ 'Redirect from before branch was a query param - github, without specified branch',
+)
+ .get('/github/badges/shields')
+ .expectRedirect('/coverallsCoverage/github/badges/shields.svg')
+
+t.create(
+ 'Redirect from before branch was a query param - bitbucket, with specified branch',
+)
+ .get('/bitbucket/pyKLIP/pyklip/master.svg')
+ .expectRedirect(
+ '/coverallsCoverage/bitbucket/pyKLIP/pyklip.svg?branch=master',
+ )
+
+t.create(
+ 'Redirect from before branch was a query param - bitbucket, without specified branch',
+)
+ .get('/bitbucket/pyKLIP/pyklip.svg')
+ .expectRedirect('/coverallsCoverage/bitbucket/pyKLIP/pyklip.svg')
diff --git a/services/coveralls/coveralls.service.js b/services/coveralls/coveralls.service.js
index a263803040d9c..2e5113425865e 100644
--- a/services/coveralls/coveralls.service.js
+++ b/services/coveralls/coveralls.service.js
@@ -1,70 +1,42 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
const schema = Joi.object({
- covered_percent: Joi.number()
- .min(0)
- .max(100)
- .required(),
+ covered_percent: Joi.number().min(0).max(100).required(),
}).required()
-module.exports = class Coveralls extends BaseJsonService {
- static get category() {
- return 'coverage'
- }
+const queryParamSchema = Joi.object({
+ branch: Joi.string(),
+}).required()
- static get route() {
- return {
- base: 'coveralls',
- pattern: ':vcsType(github|bitbucket)?/:user/:repo/:branch*',
- }
+export default class Coveralls extends BaseJsonService {
+ static category = 'coverage'
+ static route = {
+ base: 'coverallsCoverage',
+ pattern: ':vcsType(github|bitbucket|gitlab)/:user/:repo+',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Coveralls github',
- pattern: ':vcsType/:user/:repo',
- namedParams: { vcsType: 'github', user: 'jekyll', repo: 'jekyll' },
- staticPreview: this.render({ coverage: 86 }),
+ static openApi = {
+ '/coverallsCoverage/{vcsType}/{user}/{repo}': {
+ get: {
+ summary: 'Coveralls',
+ parameters: [
+ pathParam({
+ name: 'vcsType',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ }),
+ pathParam({ name: 'user', example: 'jekyll' }),
+ pathParam({ name: 'repo', example: 'jekyll' }),
+ queryParam({ name: 'branch', example: 'master' }),
+ ],
},
- {
- title: 'Coveralls github branch',
- pattern: ':vcsType/:user/:repo/:branch',
- namedParams: {
- vcsType: 'github',
- user: 'lemurheavy',
- repo: 'coveralls-ruby',
- branch: 'master',
- },
- staticPreview: this.render({ coverage: 91.81 }),
- },
- {
- title: 'Coveralls bitbucket',
- pattern: ':vcsType/:user/:repo',
- namedParams: { vcsType: 'bitbucket', user: 'pyKLIP', repo: 'pyklip' },
- staticPreview: this.render({ coverage: 86 }),
- },
- {
- title: 'Coveralls bitbucket branch',
- pattern: ':vcsType/:user/:repo/:branch',
- namedParams: {
- vcsType: 'bitbucket',
- user: 'pyKLIP',
- repo: 'pyklip',
- branch: 'master',
- },
- staticPreview: this.render({ coverage: 96 }),
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'coverage' }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ coverage }) {
return {
@@ -75,10 +47,11 @@ module.exports = class Coveralls extends BaseJsonService {
async fetch({ vcsType, user, repo, branch }) {
// https://docs.coveralls.io/api-introduction#getting-data-from-coveralls
- const url = `https://coveralls.io/${vcsType ||
- 'github'}/${user}/${repo}.json`
+ const url = `https://coveralls.io/${
+ vcsType || 'github'
+ }/${user}/${repo}.json`
const options = {
- qs: {
+ searchParams: {
// The API returns the latest result (across any branch) if no branch is explicitly specified,
// whereas the Coveralls native badge (and the Shields.io badges for Coveralls) show
// the coverage for the default branch if no branch is explicitly specified. If the user
@@ -93,13 +66,13 @@ module.exports = class Coveralls extends BaseJsonService {
schema,
url,
options,
- errorMessages: {
+ httpErrors: {
404: 'repository not found',
},
})
}
- async handle({ vcsType, user, repo, branch }) {
+ async handle({ vcsType, user, repo }, { branch }) {
const json = await this.fetch({ vcsType, user, repo, branch })
return this.constructor.render({ coverage: json.covered_percent })
}
diff --git a/services/coveralls/coveralls.tester.js b/services/coveralls/coveralls.tester.js
index 9f166002d08a5..c7cbdd0c93fe7 100644
--- a/services/coveralls/coveralls.tester.js
+++ b/services/coveralls/coveralls.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('github coverage')
.get('/github/jekyll/jekyll.json')
@@ -12,13 +11,21 @@ t.create('nonexistent project')
.expectBadge({ label: 'coverage', message: 'repository not found' })
t.create('github branch coverage')
- .get('/github/lemurheavy/coveralls-ruby/master.json')
- .expectBadge({ label: 'coverage', message: isIntegerPercentage })
-
-t.create('github coverage for legacy link')
- .get('/jekyll/jekyll.json')
+ .get('/github/lemurheavy/coveralls-ruby.json?branch=master')
.expectBadge({ label: 'coverage', message: isIntegerPercentage })
t.create('bitbucket coverage')
.get('/bitbucket/pyKLIP/pyklip.json')
.expectBadge({ label: 'coverage', message: isIntegerPercentage })
+
+t.create('bitbucket branch coverage')
+ .get('/bitbucket/pyKLIP/pyklip.json?branch=master')
+ .expectBadge({ label: 'coverage', message: isIntegerPercentage })
+
+t.create('gitlab coverage')
+ .get('/gitlab/selcouth/wsrouter.json')
+ .expectBadge({ label: 'coverage', message: isIntegerPercentage })
+
+t.create('gitlab branch coverage')
+ .get('/gitlab/selcouth/wsrouter.json?branch=master')
+ .expectBadge({ label: 'coverage', message: isIntegerPercentage })
diff --git a/services/coverity/coverity-on-demand.service.js b/services/coverity/coverity-on-demand.service.js
deleted file mode 100644
index 6f6d0b52b4a4f..0000000000000
--- a/services/coverity/coverity-on-demand.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- route: {
- base: 'coverity/ondemand',
- pattern: ':various+',
- },
- label: 'coverity',
- category: 'analysis',
- dateAdded: new Date('2018-12-18'),
-})
diff --git a/services/coverity/coverity-on-demand.tester.js b/services/coverity/coverity-on-demand.tester.js
deleted file mode 100644
index 09199d915029a..0000000000000
--- a/services/coverity/coverity-on-demand.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'CoverityOnDemand',
- title: 'Coverity On Demand',
- pathPrefix: '/coverity/ondemand',
-}))
-
-t.create('no longer available (streams)')
- .get('/streams/44b25sjc9l3ntc2ngfi29tngro.json')
- .expectBadge({
- label: 'coverity',
- message: 'no longer available',
- })
-
-t.create('no longer available (jobs)')
- .get('/jobs/p4tmm8031t4i971r0im4s7lckk.json')
- .expectBadge({
- label: 'coverity',
- message: 'no longer available',
- })
diff --git a/services/coverity/coverity-scan.service.js b/services/coverity/coverity-scan.service.js
index ac8e95b5097f0..f8e2605e470c9 100644
--- a/services/coverity/coverity-scan.service.js
+++ b/services/coverity/coverity-scan.service.js
@@ -1,46 +1,28 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
const messageRegex = /passed|passed .* new defects|pending|failed/
const schema = Joi.object({
- message: Joi.string()
- .regex(messageRegex)
- .required(),
+ message: Joi.string().regex(messageRegex).required(),
}).required()
-module.exports = class CoverityScan extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'coverity/scan',
- pattern: ':projectId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Coverity Scan',
- namedParams: {
- projectId: '3997',
- },
- staticPreview: this.render({
- message: 'passed',
+export default class CoverityScan extends BaseJsonService {
+ static category = 'analysis'
+ static route = { base: 'coverity/scan', pattern: ':projectId' }
+
+ static openApi = {
+ '/coverity/scan/{projectId}': {
+ get: {
+ summary: 'Coverity Scan',
+ parameters: pathParams({
+ name: 'projectId',
+ example: '3997',
}),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'coverity',
- }
- }
+ static defaultBadgeData = { label: 'coverity' }
static render({ message }) {
let color
@@ -66,15 +48,7 @@ module.exports = class CoverityScan extends BaseJsonService {
const json = await this._requestJson({
url,
schema,
- options: {
- // Coverity has an issue in their certificate chain that requires
- // disabling the default strict SSL check in order to call their API.
- // For more information see:
- // https://github.com/badges/shields/issues/3334
- // https://github.com/badges/shields/pull/3336
- strictSSL: false,
- },
- errorMessages: {
+ httpErrors: {
// At the moment Coverity returns an HTTP 200 with an HTML page
// displaying the text 404 when project is not found.
404: 'project not found',
diff --git a/services/coverity/coverity-scan.tester.js b/services/coverity/coverity-scan.tester.js
index 3801257acab81..4707bfa07da35 100644
--- a/services/coverity/coverity-scan.tester.js
+++ b/services/coverity/coverity-scan.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('known project id')
.get('/3997.json')
@@ -12,26 +11,21 @@ t.create('known project id')
t.create('unknown project id')
.get('/abc.json')
- // Coverity actually returns an HTTP 200 status with an HTML page when the project is not found.
- .expectBadge({ label: 'coverity', message: 'unparseable json response' })
+ .expectBadge({ label: 'coverity', message: 'project not found' })
t.create('404 response')
.get('/1.json')
.intercept(nock =>
- nock('https://scan.coverity.com/projects/1')
- .get('/badge.json')
- .reply(404)
+ nock('https://scan.coverity.com/projects/1').get('/badge.json').reply(404),
)
.expectBadge({ label: 'coverity', message: 'project not found' })
t.create('passed')
.get('/2.json')
.intercept(nock =>
- nock('https://scan.coverity.com/projects/2')
- .get('/badge.json')
- .reply(200, {
- message: 'passed',
- })
+ nock('https://scan.coverity.com/projects/2').get('/badge.json').reply(200, {
+ message: 'passed',
+ }),
)
.expectBadge({
label: 'coverity',
@@ -42,11 +36,9 @@ t.create('passed')
t.create('passed with defects')
.get('/2.json')
.intercept(nock =>
- nock('https://scan.coverity.com/projects/2')
- .get('/badge.json')
- .reply(200, {
- message: 'passed 51 new defects',
- })
+ nock('https://scan.coverity.com/projects/2').get('/badge.json').reply(200, {
+ message: 'passed 51 new defects',
+ }),
)
.expectBadge({
label: 'coverity',
@@ -57,11 +49,9 @@ t.create('passed with defects')
t.create('pending')
.get('/2.json')
.intercept(nock =>
- nock('https://scan.coverity.com/projects/2')
- .get('/badge.json')
- .reply(200, {
- message: 'pending',
- })
+ nock('https://scan.coverity.com/projects/2').get('/badge.json').reply(200, {
+ message: 'pending',
+ }),
)
.expectBadge({
label: 'coverity',
@@ -72,11 +62,9 @@ t.create('pending')
t.create('failed')
.get('/2.json')
.intercept(nock =>
- nock('https://scan.coverity.com/projects/2')
- .get('/badge.json')
- .reply(200, {
- message: 'failed',
- })
+ nock('https://scan.coverity.com/projects/2').get('/badge.json').reply(200, {
+ message: 'failed',
+ }),
)
.expectBadge({
label: 'coverity',
diff --git a/services/cpan/cpan-license.service.js b/services/cpan/cpan-license.service.js
index 6e1faff497f50..d67c721a50f86 100644
--- a/services/cpan/cpan-license.service.js
+++ b/services/cpan/cpan-license.service.js
@@ -1,28 +1,21 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { BaseCpanService, description } from './cpan.js'
-const BaseCpanService = require('./cpan')
+export default class CpanLicense extends BaseCpanService {
+ static category = 'license'
+ static route = { base: 'cpan/l', pattern: ':packageName' }
-module.exports = class CpanLicense extends BaseCpanService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'cpan/l',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'CPAN',
- namedParams: { packageName: 'Config-Augeas' },
- staticPreview: this.render({ license: 'lgpl_2_1' }),
- keywords: ['perl'],
+ static openApi = {
+ '/cpan/l/{packageName}': {
+ get: {
+ summary: 'CPAN License',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'Config-Augeas',
+ }),
},
- ]
+ },
}
static render({ license }) {
diff --git a/services/cpan/cpan-license.tester.js b/services/cpan/cpan-license.tester.js
index c841b2b2fbb0f..8cfba3c5e1960 100644
--- a/services/cpan/cpan-license.tester.js
+++ b/services/cpan/cpan-license.tester.js
@@ -1,17 +1,12 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('license (valid)').get('/Config-Augeas.json').expectBadge({
+ label: 'license',
+ message: 'lgpl_2_1',
+})
-t.create('license (valid)')
- .get('/Config-Augeas.json')
- .expectBadge({
- label: 'license',
- message: 'lgpl_2_1',
- })
-
-t.create('license (not found)')
- .get('/not-a-package.json')
- .expectBadge({
- label: 'cpan',
- message: 'not found',
- })
+t.create('license (not found)').get('/not-a-package.json').expectBadge({
+ label: 'cpan',
+ message: 'not found',
+})
diff --git a/services/cpan/cpan-version.service.js b/services/cpan/cpan-version.service.js
index 57a60bda4ce16..6971a3df814c2 100644
--- a/services/cpan/cpan-version.service.js
+++ b/services/cpan/cpan-version.service.js
@@ -1,29 +1,22 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseCpanService, description } from './cpan.js'
-const { renderVersionBadge } = require('../version')
-const BaseCpanService = require('./cpan')
+export default class CpanVersion extends BaseCpanService {
+ static category = 'version'
+ static route = { base: 'cpan/v', pattern: ':packageName' }
-module.exports = class CpanVersion extends BaseCpanService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'cpan/v',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'CPAN',
- namedParams: { packageName: 'Config-Augeas' },
- staticPreview: renderVersionBadge({ version: '1.000' }),
- keywords: ['perl'],
+ static openApi = {
+ '/cpan/v/{packageName}': {
+ get: {
+ summary: 'CPAN Version',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'Config-Augeas',
+ }),
},
- ]
+ },
}
async handle({ packageName }) {
diff --git a/services/cpan/cpan-version.tester.js b/services/cpan/cpan-version.tester.js
index 1a1e26fa8a5ef..c2a64ca474b1d 100644
--- a/services/cpan/cpan-version.tester.js
+++ b/services/cpan/cpan-version.tester.js
@@ -1,18 +1,28 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('version (valid)').get('/Config-Augeas.json').expectBadge({
+ label: 'cpan',
+ message: isVPlusDottedVersionAtLeastOne,
+})
-t.create('version (valid)')
+t.create('version (version is a number rather than a string)')
.get('/Config-Augeas.json')
+ .intercept(nock =>
+ nock('https://fastapi.metacpan.org')
+ .get('/v1/release/Config-Augeas')
+ .reply(200, {
+ license: ['GPL'],
+ version: 0.88,
+ }),
+ )
.expectBadge({
label: 'cpan',
- message: isVPlusDottedVersionAtLeastOne,
+ message: 'v0.88',
})
-t.create('version (not found)')
- .get('/not-a-package.json')
- .expectBadge({
- label: 'cpan',
- message: 'not found',
- })
+t.create('version (not found)').get('/not-a-package.json').expectBadge({
+ label: 'cpan',
+ message: 'not found',
+})
diff --git a/services/cpan/cpan.js b/services/cpan/cpan.js
index 818b0f80e4125..de24f4d7ddbfa 100644
--- a/services/cpan/cpan.js
+++ b/services/cpan/cpan.js
@@ -1,23 +1,21 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
- version: Joi.string().required(),
- license: Joi.array()
- .items(Joi.string())
- .min(1)
- .required(),
+ version: Joi.alternatives(Joi.string().required(), Joi.number().required()),
+ license: Joi.array().items(Joi.string()).min(1).required(),
}).required()
-module.exports = class BaseCpanService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'cpan' }
- }
+const description =
+ '[CPAN](https://www.cpan.org/) is a package registry for Perl'
+
+export default class BaseCpanService extends BaseJsonService {
+ static defaultBadgeData = { label: 'cpan' }
async fetch({ packageName }) {
const url = `https://fastapi.metacpan.org/v1/release/${packageName}`
return this._requestJson({ schema, url })
}
}
+
+export { BaseCpanService, description }
diff --git a/services/cran/cran.service.js b/services/cran/cran.service.js
index 73095370bfbee..369e37d11910c 100644
--- a/services/cran/cran.service.js
+++ b/services/cran/cran.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
License: Joi.string().required(),
@@ -10,9 +8,7 @@ const schema = Joi.object({
}).required()
class BaseCranService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'cran' }
- }
+ static defaultBadgeData = { label: 'cran' }
async fetch({ packageName }) {
const url = `http://crandb.r-pkg.org/${packageName}`
@@ -21,25 +17,19 @@ class BaseCranService extends BaseJsonService {
}
class CranLicense extends BaseCranService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'cran/l',
- pattern: ':packageName',
- }
- }
+ static category = 'license'
+ static route = { base: 'cran/l', pattern: ':packageName' }
- static get examples() {
- return [
- {
- title: 'CRAN/METACRAN',
- namedParams: { packageName: 'devtools' },
- staticPreview: this.render({ license: 'GPL (>= 2)' }),
+ static openApi = {
+ '/cran/l/{packageName}': {
+ get: {
+ summary: 'CRAN/METACRAN License',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'devtools',
+ }),
},
- ]
+ },
}
static render({ license }) {
@@ -57,25 +47,19 @@ class CranLicense extends BaseCranService {
}
class CranVersion extends BaseCranService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'cran/v',
- pattern: ':packageName',
- }
- }
+ static category = 'version'
+ static route = { base: 'cran/v', pattern: ':packageName' }
- static get examples() {
- return [
- {
- title: 'CRAN/METACRAN',
- namedParams: { packageName: 'devtools' },
- staticPreview: this.render({ version: '2.0.1' }),
+ static openApi = {
+ '/cran/v/{packageName}': {
+ get: {
+ summary: 'CRAN/METACRAN Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'devtools',
+ }),
},
- ]
+ },
}
static render({ version }) {
@@ -88,4 +72,4 @@ class CranVersion extends BaseCranService {
}
}
-module.exports = { CranLicense, CranVersion }
+export { CranLicense, CranVersion }
diff --git a/services/cran/cran.tester.js b/services/cran/cran.tester.js
index 92f65d1a7b7b8..1581c8a7ddc70 100644
--- a/services/cran/cran.tester.js
+++ b/services/cran/cran.tester.js
@@ -1,19 +1,15 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'cran',
title: 'CRAN/METACRAN',
-}))
+})
-t.create('version (valid)')
- .get('/v/devtools.json')
- .expectBadge({
- label: 'cran',
- message: isVPlusTripleDottedVersion,
- })
+t.create('version (valid)').get('/v/devtools.json').expectBadge({
+ label: 'cran',
+ message: isVPlusTripleDottedVersion,
+})
t.create('version (not found)')
.get('/v/some-bogus-package.json')
@@ -21,7 +17,7 @@ t.create('version (not found)')
t.create('license (valid)')
.get('/l/devtools.json')
- .expectBadge({ label: 'license', message: 'GPL (>= 2)' })
+ .expectBadge({ label: 'license', message: 'MIT + file LICENSE' })
t.create('license (not found)')
.get('/l/some-bogus-package.json')
diff --git a/services/crates/crates-base.js b/services/crates/crates-base.js
index 00a5742378426..e671fe056dd7e 100644
--- a/services/crates/crates-base.js
+++ b/services/crates/crates-base.js
@@ -1,56 +1,91 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, InvalidResponse } from '../index.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const keywords = ['Rust']
+const versionSchema = Joi.object({
+ downloads: nonNegativeInteger,
+ crate_size: nonNegativeInteger,
+ num: Joi.string().required(),
+ license: Joi.string().required().allow(null),
+ rust_version: Joi.string().allow(null),
+})
-const crateSchema = Joi.object({
+const crateResponseSchema = Joi.object({
crate: Joi.object({
downloads: nonNegativeInteger,
- recent_downloads: nonNegativeInteger,
+ recent_downloads: nonNegativeInteger.allow(null),
max_version: Joi.string().required(),
}).required(),
- versions: Joi.array()
- .items(
- Joi.object({
- downloads: nonNegativeInteger,
- license: Joi.string().required(),
- })
- )
- .min(1)
- .required(),
+ versions: Joi.array().items(versionSchema).min(1).required(),
}).required()
-const versionSchema = Joi.object({
- version: Joi.object({
- downloads: nonNegativeInteger,
- num: Joi.string().required(),
- license: Joi.string().required(),
- }).required(),
+const versionResponseSchema = Joi.object({
+ version: versionSchema.required(),
}).required()
-const errorSchema = Joi.object({
- errors: Joi.array()
- .items(Joi.object({ detail: Joi.string().required() }))
- .min(1)
- .required(),
+const userStatsSchema = Joi.object({
+ total_downloads: nonNegativeInteger.required(),
}).required()
-const schema = Joi.alternatives(crateSchema, versionSchema, errorSchema)
-
class BaseCratesService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'crates.io' }
- }
+ static defaultBadgeData = { label: 'crates.io' }
+ /**
+ * Fetches data from the crates.io API.
+ *
+ * @param {object} options - The options for the request
+ * @param {string} options.crate - The crate name.
+ * @param {string} [options.version] - The crate version number (optional).
+ * @returns {Promise} the JSON response from the API.
+ */
async fetch({ crate, version }) {
const url = version
? `https://crates.io/api/v1/crates/${crate}/${version}`
- : `https://crates.io/api/v1/crates/${crate}`
- return this._requestJson({ schema, url })
+ : `https://crates.io/api/v1/crates/${crate}?include=versions,downloads`
+ const schema = version ? versionResponseSchema : crateResponseSchema
+ return this._requestJson({
+ schema,
+ url,
+ httpErrors: version ? { 400: 'invalid version' } : {},
+ })
+ }
+
+ static getLatestVersion(response) {
+ return response.crate.max_stable_version
+ ? response.crate.max_stable_version
+ : response.crate.max_version
+ }
+
+ static getVersionObj(response) {
+ if (response.crate) {
+ const version = this.getLatestVersion(response)
+ const versionObj = response.versions.find(v => v.num === version)
+ if (versionObj === undefined) {
+ throw new InvalidResponse({ prettyMessage: 'version not found' })
+ }
+ return versionObj
+ }
+ return response.version
+ }
+}
+
+class BaseCratesUserService extends BaseJsonService {
+ static defaultBadgeData = { label: 'crates.io' }
+
+ /**
+ * Fetches data from the crates.io API.
+ *
+ * @param {object} options - The options for the request
+ * @param {string} options.userId - The user ID.
+ * @returns {Promise} the JSON response from the API.
+ */
+ async fetch({ userId }) {
+ const url = `https://crates.io/api/v1/users/${userId}/stats`
+ return this._requestJson({ schema: userStatsSchema, url })
}
}
-module.exports = { BaseCratesService, keywords }
+const description =
+ '[Crates.io](https://crates.io/) is a package registry for Rust.'
+
+export { BaseCratesService, BaseCratesUserService, description }
diff --git a/services/crates/crates-base.spec.js b/services/crates/crates-base.spec.js
new file mode 100644
index 0000000000000..a68990acb132d
--- /dev/null
+++ b/services/crates/crates-base.spec.js
@@ -0,0 +1,47 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { BaseCratesService } from './crates-base.js'
+
+describe('BaseCratesService', function () {
+ describe('getLatestVersion', function () {
+ test(BaseCratesService.getLatestVersion, () => {
+ given({ crate: { max_version: '1.1.0' } }).expect('1.1.0')
+ given({
+ crate: { max_stable_version: '1.1.0', max_version: '1.9.0-alpha' },
+ }).expect('1.1.0')
+ })
+ })
+
+ describe('getVersionObj', function () {
+ const versions = [
+ /*
+ These versions are more recent, but we're going to ignore them
+ That might be because they were yanked, or because they're pre-releases.
+ */
+ { num: '1.3.0-beta' },
+ { num: '1.2.0' },
+ // this is the one we will select
+ { num: '1.1.0' },
+ ]
+
+ it('ignores more recent versions than max_stable_version', function () {
+ const response = {
+ crate: {
+ max_stable_version: '1.1.0',
+ },
+ versions,
+ }
+ expect(BaseCratesService.getVersionObj(response).num).to.equal('1.1.0')
+ })
+
+ it('ignores more recent versions than max_version', function () {
+ const response = {
+ crate: {
+ max_version: '1.1.0',
+ },
+ versions,
+ }
+ expect(BaseCratesService.getVersionObj(response).num).to.equal('1.1.0')
+ })
+ })
+})
diff --git a/services/crates/crates-dependents.service.js b/services/crates/crates-dependents.service.js
new file mode 100644
index 0000000000000..8b950b87be256
--- /dev/null
+++ b/services/crates/crates-dependents.service.js
@@ -0,0 +1,51 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseCratesService, description } from './crates-base.js'
+
+const dependentsResponseSchema = Joi.object({
+ meta: Joi.object({
+ total: nonNegativeInteger,
+ }).required(),
+}).required()
+
+export default class CratesDependents extends BaseCratesService {
+ static category = 'other'
+ static route = { base: 'crates/dependents', pattern: ':crate' }
+
+ static openApi = {
+ '/crates/dependents/{crate}': {
+ get: {
+ summary: 'Crates.io Dependents',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'tokio',
+ }),
+ },
+ },
+ }
+
+ static _cacheLength = 43200 // due to issue #11633 - cache for 12 hours - this value is not quick to change
+
+ static render({ dependentCount }) {
+ return {
+ label: 'dependents',
+ message: metric(dependentCount),
+ color: dependentCount === 0 ? 'orange' : 'brightgreen',
+ }
+ }
+
+ async fetch({ crate }) {
+ const url = `https://crates.io/api/v1/crates/${crate}/reverse_dependencies`
+ const schema = dependentsResponseSchema
+ return this._requestJson({ schema, url })
+ }
+
+ async handle({ crate }) {
+ const json = await this.fetch({ crate })
+ const { total: dependentCount } = json.meta
+ return this.constructor.render({ dependentCount })
+ }
+}
diff --git a/services/crates/crates-dependents.tester.js b/services/crates/crates-dependents.tester.js
new file mode 100644
index 0000000000000..088bb6818ee41
--- /dev/null
+++ b/services/crates/crates-dependents.tester.js
@@ -0,0 +1,15 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('dependent count').get('/base64.json').expectBadge({
+ label: 'dependents',
+ message: isMetric,
+})
+
+t.create('dependent count (nonexistent package)')
+ .get('/foobar-is-not-crate.json')
+ .expectBadge({
+ label: 'crates.io',
+ message: 'not found',
+ })
diff --git a/services/crates/crates-downloads.service.js b/services/crates/crates-downloads.service.js
index 4363716dac7c4..1abda7b6e838b 100644
--- a/services/crates/crates-downloads.service.js
+++ b/services/crates/crates-downloads.service.js
@@ -1,93 +1,83 @@
-'use strict'
+import { renderDownloadsBadge } from '../downloads.js'
+import { InvalidParameter, pathParams } from '../index.js'
+import { BaseCratesService, description } from './crates-base.js'
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { InvalidParameter, NotFound } = require('..')
-const { BaseCratesService, keywords } = require('./crates-base')
-
-module.exports = class CratesDownloads extends BaseCratesService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'crates',
- pattern: ':variant(d|dv|dr)/:crate/:version?',
- }
+export default class CratesDownloads extends BaseCratesService {
+ static category = 'downloads'
+ static route = {
+ base: 'crates',
+ pattern: ':variant(d|dv|dr)/:crate/:version?',
}
- static get examples() {
- return [
- {
- title: 'Crates.io',
- pattern: 'd/:crate',
- namedParams: {
- crate: 'rustc-serialize',
- },
- staticPreview: this.render({ variant: 'd', downloads: 5000000 }),
- keywords,
- },
- {
- title: 'Crates.io (latest)',
- pattern: 'dv/:crate',
- namedParams: {
- crate: 'rustc-serialize',
- },
- staticPreview: this.render({ variant: 'dv', downloads: 2000000 }),
- keywords,
+ static openApi = {
+ '/crates/d/{crate}': {
+ get: {
+ summary: 'Crates.io Total Downloads',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
+ }),
},
- {
- title: 'Crates.io (version)',
- pattern: 'dv/:crate/:version',
- namedParams: {
- crate: 'rustc-serialize',
- version: '0.3.24',
- },
- staticPreview: this.render({
- variant: 'dv',
- downloads: 2000000,
- version: '0.3.24',
+ },
+ '/crates/dv/{crate}': {
+ get: {
+ summary: 'Crates.io Downloads (latest version)',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
}),
- keywords,
},
- {
- title: 'Crates.io (recent)',
- pattern: 'dr/:crate',
- namedParams: {
- crate: 'rustc-serialize',
- },
- staticPreview: this.render({ variant: 'dr', downloads: 2000000 }),
- keywords,
+ },
+ '/crates/dv/{crate}/{version}': {
+ get: {
+ summary: 'Crates.io Downloads (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'rustc-serialize',
+ },
+ {
+ name: 'version',
+ example: '0.3.24',
+ },
+ ),
+ },
+ },
+ '/crates/dr/{crate}': {
+ get: {
+ summary: 'Crates.io Downloads (recent)',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
+ }),
},
- ]
+ },
}
- static _getLabel(version, variant) {
- switch (variant) {
- case 'dv':
- return version ? `downloads@${version}` : 'downloads@latest'
- case 'dr':
- return 'recent downloads'
- default:
- return version ? `downloads@${version}` : 'downloads'
- }
- }
+ static _cacheLength = 3600 // We're hitting the API more frequently than requested by upstream maintainers (see https://github.com/badges/shields/issues/11879).
static render({ variant, downloads, version }) {
- return {
- label: this._getLabel(version, variant),
- message: metric(downloads),
- color: downloadCountColor(downloads),
+ let labelOverride
+ if (variant === 'dr') {
+ labelOverride = 'recent downloads'
+ } else if (variant === 'dv' && !version) {
+ version = 'latest'
+ } else if (!version) {
+ labelOverride = 'downloads'
}
+ return renderDownloadsBadge({ downloads, labelOverride, version })
}
transform({ variant, json }) {
switch (variant) {
case 'dv':
- return json.crate ? json.versions[0].downloads : json.version.downloads
+ return this.constructor.getVersionObj(json).downloads
case 'dr':
- return json.crate.recent_downloads
+ return json.crate.recent_downloads || 0
default:
return json.crate ? json.crate.downloads : json.version.downloads
}
@@ -105,15 +95,6 @@ module.exports = class CratesDownloads extends BaseCratesService {
const json = await this.fetch({ crate, version })
- if (json.errors) {
- /* a call like
- https://crates.io/api/v1/crates/libc/0.1
- or
- https://crates.io/api/v1/crates/libc/0.1.76
- returns a 200 OK with an errors object */
- throw new NotFound({ prettyMessage: json.errors[0].detail })
- }
-
const downloads = this.transform({ variant, json })
return this.constructor.render({ variant, downloads, version })
diff --git a/services/crates/crates-downloads.tester.js b/services/crates/crates-downloads.tester.js
index b56f956255a88..d03367dbf26df 100644
--- a/services/crates/crates-downloads.tester.js
+++ b/services/crates/crates-downloads.tester.js
@@ -1,13 +1,6 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { isMetric } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
- id: 'crates',
- title: 'crates.io',
- pathPrefix: '/crates',
-}))
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('total downloads')
.get('/d/libc.json')
@@ -20,12 +13,10 @@ t.create('total downloads (with version)')
message: isMetric,
})
-t.create('downloads for version')
- .get('/dv/libc.json')
- .expectBadge({
- label: 'downloads@latest',
- message: isMetric,
- })
+t.create('downloads for version').get('/dv/libc.json').expectBadge({
+ label: 'downloads@latest',
+ message: isMetric,
+})
t.create('downloads for version (with version)')
.get('/dv/libc/0.2.31.json')
@@ -34,12 +25,33 @@ t.create('downloads for version (with version)')
message: isMetric,
})
-t.create('recent downloads')
+t.create('recent downloads').get('/dr/libc.json').expectBadge({
+ label: 'recent downloads',
+ message: isMetric,
+})
+
+t.create('recent downloads (null)')
.get('/dr/libc.json')
- .expectBadge({
- label: 'recent downloads',
- message: isMetric,
- })
+ .intercept(nock =>
+ nock('https://crates.io')
+ .get('/api/v1/crates/libc?include=versions,downloads')
+ .reply(200, {
+ crate: {
+ downloads: 42,
+ recent_downloads: null,
+ max_version: '0.2.71',
+ },
+ versions: [
+ {
+ downloads: 42,
+ license: 'MIT OR Apache-2.0',
+ num: '0.2.71',
+ crate_size: 42,
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'recent downloads', message: '0' })
t.create('recent downloads (with version)')
.get('/dr/libc/0.2.31.json')
@@ -48,9 +60,13 @@ t.create('recent downloads (with version)')
message: 'recent downloads not supported for specific versions',
})
+t.create('downloads (non-existent version)')
+ .get('/d/libc/7.0.0.json')
+ .expectBadge({ label: 'crates.io', message: 'not found' })
+
t.create('downloads (invalid version)')
- .get('/d/libc/7.json')
- .expectBadge({ label: 'crates.io', message: 'invalid semver: 7' })
+ .get('/d/libc/7.json') // Does not follow semver
+ .expectBadge({ label: 'crates.io', message: 'invalid version' })
t.create('downloads (not found)')
.get('/d/not-a-real-package.json')
diff --git a/services/crates/crates-license.service.js b/services/crates/crates-license.service.js
index 2b779356167e0..9dc52e7ac022b 100644
--- a/services/crates/crates-license.service.js
+++ b/services/crates/crates-license.service.js
@@ -1,60 +1,53 @@
-'use strict'
-
-const { BaseCratesService, keywords } = require('./crates-base')
-
-module.exports = class CratesLicense extends BaseCratesService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'crates/l',
- pattern: ':crate/:version?',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Crates.io',
- pattern: ':crate',
- namedParams: { crate: 'rustc-serialize' },
- staticPreview: this.render({ license: 'MIT/Apache-2.0' }),
- keywords,
+import { InvalidResponse, pathParams } from '../index.js'
+import { BaseCratesService, description } from './crates-base.js'
+
+export default class CratesLicense extends BaseCratesService {
+ static category = 'license'
+ static route = { base: 'crates/l', pattern: ':crate/:version?' }
+
+ static openApi = {
+ '/crates/l/{crate}': {
+ get: {
+ summary: 'Crates.io License',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
+ }),
},
- {
- title: 'Crates.io',
- pattern: ':crate/:version',
- namedParams: { crate: 'rustc-serialize', version: '0.3.24' },
- staticPreview: this.render({ license: 'MIT/Apache-2.0' }),
- keywords,
+ },
+ '/crates/l/{crate}/{version}': {
+ get: {
+ summary: 'Crates.io License (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'rustc-serialize',
+ },
+ {
+ name: 'version',
+ example: '0.3.24',
+ },
+ ),
},
- ]
+ },
}
- static render({ license }) {
- return {
- label: 'license',
- message: license,
- color: 'blue',
+ static defaultBadgeData = { label: 'license', color: 'blue' }
+
+ static transform(response) {
+ const license = this.getVersionObj(response).license
+ if (!license) {
+ throw new InvalidResponse({ prettyMessage: 'invalid null license' })
}
+
+ return { license }
}
async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
-
- if (json.errors) {
- /* a call like
- https://crates.io/api/v1/crates/libc/0.1
- or
- https://crates.io/api/v1/crates/libc/0.1.76
- returns a 200 OK with an errors object */
- return { message: json.errors[0].detail }
- }
-
- return this.constructor.render({
- license: json.version ? json.version.license : json.versions[0].license,
- })
+ const { license } = this.constructor.transform(json)
+ return { message: license }
}
}
diff --git a/services/crates/crates-license.spec.js b/services/crates/crates-license.spec.js
new file mode 100644
index 0000000000000..19424ab1d9615
--- /dev/null
+++ b/services/crates/crates-license.spec.js
@@ -0,0 +1,39 @@
+import { expect } from 'chai'
+import { InvalidResponse } from '../index.js'
+import CratesLicense from './crates-license.service.js'
+
+describe('CratesLicense', function () {
+ it('extracts expected license given valid inputs', function () {
+ expect(
+ CratesLicense.transform({
+ version: { num: '1.0.0', license: 'MIT' },
+ }),
+ ).to.deep.equal({ license: 'MIT' })
+
+ expect(
+ CratesLicense.transform({
+ crate: { max_stable_version: '1.2.3' },
+ versions: [{ num: '1.2.3', license: 'MIT/Apache 2.0' }],
+ }),
+ ).to.deep.equal({ license: 'MIT/Apache 2.0' })
+ })
+
+ it('throws InvalidResponse on null license with specific version', function () {
+ expect(() =>
+ CratesLicense.transform({ version: { num: '1.2.3', license: null } }),
+ )
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'invalid null license')
+ })
+
+ it('throws InvalidResponse on null license with latest version', function () {
+ expect(() =>
+ CratesLicense.transform({
+ crate: { max_stable_version: '1.2.3' },
+ versions: [{ num: '1.2.3', license: null }],
+ }),
+ )
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'invalid null license')
+ })
+})
diff --git a/services/crates/crates-license.tester.js b/services/crates/crates-license.tester.js
index 79d711034ea09..6351c14f85635 100644
--- a/services/crates/crates-license.tester.js
+++ b/services/crates/crates-license.tester.js
@@ -1,12 +1,5 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'crates',
- title: 'crates.io',
- pathPrefix: '/crates/l',
-}))
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license')
.get('/libc.json')
@@ -18,4 +11,13 @@ t.create('license (with version)')
t.create('license (not found)')
.get('/not-a-real-package.json')
- .expectBadge({ label: 'crates.io', message: 'not found' })
+ .expectBadge({ label: 'license', message: 'not found' })
+
+// https://github.com/badges/shields/issues/7073
+t.create('license (null licenses in history)')
+ .get('/stun.json')
+ .expectBadge({ label: 'license', message: 'MIT/Apache-2.0' })
+
+t.create('license (version with null license)')
+ .get('/stun/0.0.1.json')
+ .expectBadge({ label: 'license', message: 'invalid null license' })
diff --git a/services/crates/crates-msrv.service.js b/services/crates/crates-msrv.service.js
new file mode 100644
index 0000000000000..ceeee0a14f449
--- /dev/null
+++ b/services/crates/crates-msrv.service.js
@@ -0,0 +1,70 @@
+import { NotFound, pathParams } from '../index.js'
+import {
+ BaseCratesService,
+ description as cratesIoDescription,
+} from './crates-base.js'
+
+const description = `
+${cratesIoDescription}
+
+MSRV is a crate's minimum suppported rust version,
+the oldest version of Rust supported by the crate.
+See the [Cargo Book](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
+for more info.
+`
+
+export default class CratesMSRV extends BaseCratesService {
+ static category = 'platform-support'
+ static route = {
+ base: 'crates/msrv',
+ pattern: ':crate/:version?',
+ }
+
+ static openApi = {
+ '/crates/msrv/{crate}': {
+ get: {
+ summary: 'Crates.io MSRV',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'serde',
+ }),
+ },
+ },
+ '/crates/msrv/{crate}/{version}': {
+ get: {
+ summary: 'Crates.io MSRV (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'serde',
+ },
+ {
+ name: 'version',
+ example: '1.0.194',
+ },
+ ),
+ },
+ },
+ }
+
+ static _cacheLength = 3600 // We're hitting the API more frequently than requested by upstream maintainers (see https://github.com/badges/shields/issues/11879).
+
+ static defaultBadgeData = { label: 'msrv', color: 'blue' }
+
+ static transform(response) {
+ const msrv = this.getVersionObj(response).rust_version
+ if (!msrv) {
+ throw new NotFound({ prettyMessage: 'unknown' })
+ }
+
+ return { msrv }
+ }
+
+ async handle({ crate, version }) {
+ const json = await this.fetch({ crate, version })
+ const { msrv } = this.constructor.transform(json)
+ return { message: msrv }
+ }
+}
diff --git a/services/crates/crates-msrv.tester.js b/services/crates/crates-msrv.tester.js
new file mode 100644
index 0000000000000..bc2ae6e35c33a
--- /dev/null
+++ b/services/crates/crates-msrv.tester.js
@@ -0,0 +1,15 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+// dummy crate i created specifically for this test case
+t.create('msrv')
+ .get('/shields-test-dummy-crate-msrv-3452398210.json')
+ .expectBadge({ label: 'msrv', message: '1.69' })
+
+t.create('msrv (with version)')
+ .get('/shields-test-dummy-crate-msrv-3452398210/0.69.0.json')
+ .expectBadge({ label: 'msrv', message: '1.69' })
+
+t.create('msrv (not found)')
+ .get('/not-a-real-package.json')
+ .expectBadge({ label: 'msrv', message: 'not found' })
diff --git a/services/crates/crates-size.service.js b/services/crates/crates-size.service.js
new file mode 100644
index 0000000000000..ec52f01cb6a3f
--- /dev/null
+++ b/services/crates/crates-size.service.js
@@ -0,0 +1,48 @@
+import { pathParams } from '../index.js'
+import { renderSizeBadge } from '../size.js'
+import { BaseCratesService, description } from './crates-base.js'
+
+export default class CratesSize extends BaseCratesService {
+ static category = 'size'
+ static route = {
+ base: 'crates/size',
+ pattern: ':crate/:version?',
+ }
+
+ static openApi = {
+ '/crates/size/{crate}': {
+ get: {
+ summary: 'Crates.io Size',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
+ }),
+ },
+ },
+ '/crates/size/{crate}/{version}': {
+ get: {
+ summary: 'Crates.io Size (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'rustc-serialize',
+ },
+ {
+ name: 'version',
+ example: '0.3.24',
+ },
+ ),
+ },
+ },
+ }
+
+ static _cacheLength = 3600 // We're hitting the API more frequently than requested by upstream maintainers (see https://github.com/badges/shields/issues/11879).
+
+ async handle({ crate, version }) {
+ const json = await this.fetch({ crate, version })
+ const size = this.constructor.getVersionObj(json).crate_size
+ return renderSizeBadge(size, 'iec')
+ }
+}
diff --git a/services/crates/crates-size.tester.js b/services/crates/crates-size.tester.js
new file mode 100644
index 0000000000000..7512aa62b784e
--- /dev/null
+++ b/services/crates/crates-size.tester.js
@@ -0,0 +1,15 @@
+import { createServiceTester } from '../tester.js'
+import { isIecFileSize } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('size')
+ .get('/tokio.json')
+ .expectBadge({ label: 'size', message: isIecFileSize })
+
+t.create('size (with version)')
+ .get('/tokio/1.32.0.json')
+ .expectBadge({ label: 'size', message: '708 KiB' })
+
+t.create('size (not found)')
+ .get('/not-a-crate.json')
+ .expectBadge({ label: 'crates.io', message: 'not found' })
diff --git a/services/crates/crates-user-downloads.service.js b/services/crates/crates-user-downloads.service.js
new file mode 100644
index 0000000000000..f30ea0a6d800a
--- /dev/null
+++ b/services/crates/crates-user-downloads.service.js
@@ -0,0 +1,34 @@
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import { BaseCratesUserService, description } from './crates-base.js'
+
+export default class CratesUserDownloads extends BaseCratesUserService {
+ static category = 'downloads'
+ static route = {
+ base: 'crates',
+ pattern: 'udt/:userId',
+ }
+
+ static openApi = {
+ '/crates/udt/{userId}': {
+ get: {
+ summary: 'Crates.io User Total Downloads',
+ description,
+ parameters: pathParams({
+ name: 'userId',
+ example: '3027',
+ description:
+ 'The user ID can be found using `https://crates.io/api/v1/users/{username}`',
+ }),
+ },
+ },
+ }
+
+ static _cacheLength = 3600 // We're hitting the API more frequently than requested by upstream maintainers (see https://github.com/badges/shields/issues/11879).
+
+ async handle({ userId }) {
+ const json = await this.fetch({ userId })
+ const { total_downloads: downloads } = json
+ return renderDownloadsBadge({ downloads, labelOverride: 'downloads' })
+ }
+}
diff --git a/services/crates/crates-user-downloads.tester.js b/services/crates/crates-user-downloads.tester.js
new file mode 100644
index 0000000000000..d544a56fe35ab
--- /dev/null
+++ b/services/crates/crates-user-downloads.tester.js
@@ -0,0 +1,16 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('total user downloads')
+ .get('/udt/3027.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+// non-existent user returns 0 downloads with 200 OK status code rather than 404.
+t.create('total user downloads (user not found)')
+ .get('/udt/2147483647.json') // 2147483647 is the maximum valid user id as API uses i32
+ .expectBadge({ label: 'downloads', message: '0' })
+
+t.create('total user downloads (invalid)')
+ .get('/udt/999999999999999999999999.json')
+ .expectBadge({ label: 'crates.io', message: 'invalid' })
diff --git a/services/crates/crates-version.service.js b/services/crates/crates-version.service.js
index 91babda795147..c7556bc06d518 100644
--- a/services/crates/crates-version.service.js
+++ b/services/crates/crates-version.service.js
@@ -1,42 +1,29 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { pathParams } from '../index.js'
+import { BaseCratesService, description } from './crates-base.js'
-const { renderVersionBadge } = require('../version')
-const { InvalidResponse } = require('..')
-const { BaseCratesService, keywords } = require('./crates-base')
+export default class CratesVersion extends BaseCratesService {
+ static category = 'version'
+ static route = { base: 'crates/v', pattern: ':crate' }
-module.exports = class CratesVersion extends BaseCratesService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'crates/v',
- pattern: ':crate',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Crates.io',
- namedParams: { crate: 'rustc-serialize' },
- staticPreview: renderVersionBadge({ version: '0.3.24' }),
- keywords,
+ static openApi = {
+ '/crates/v/{crate}': {
+ get: {
+ summary: 'Crates.io Version',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'rustc-serialize',
+ }),
},
- ]
+ },
}
- transform(json) {
- if (json.errors) {
- throw new InvalidResponse({ prettyMessage: json.errors[0].detail })
- }
- return { version: json.version ? json.version.num : json.crate.max_version }
- }
+ static _cacheLength = 3600 // We're hitting the API more frequently than requested by upstream maintainers (see https://github.com/badges/shields/issues/11879).
async handle({ crate }) {
const json = await this.fetch({ crate })
- const { version } = this.transform(json)
+ const version = this.constructor.getLatestVersion(json)
return renderVersionBadge({ version })
}
}
diff --git a/services/crates/crates-version.spec.js b/services/crates/crates-version.spec.js
deleted file mode 100644
index e1c13e3eca108..0000000000000
--- a/services/crates/crates-version.spec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const { expect } = require('chai')
-const { InvalidResponse } = require('..')
-const CratesVersion = require('./crates-version.service')
-
-describe('CratesVersion', function() {
- test(CratesVersion.prototype.transform, () => {
- given({ version: { num: '1.0.0' } }).expect({ version: '1.0.0' })
- given({ crate: { max_version: '1.1.0' } }).expect({ version: '1.1.0' })
- })
-
- it('throws InvalidResponse on error response', function() {
- expect(() =>
- CratesVersion.prototype.transform({ errors: [{ detail: 'idk how...' }] })
- ).to.throw(InvalidResponse)
- })
-})
diff --git a/services/crates/crates-version.tester.js b/services/crates/crates-version.tester.js
index 4fc19952d8bd0..3242c1cd5a82e 100644
--- a/services/crates/crates-version.tester.js
+++ b/services/crates/crates-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('version')
.get('/libc.json')
diff --git a/services/ctan/ctan.service.js b/services/ctan/ctan.service.js
index 342bbfa924eef..0f63df946074e 100644
--- a/services/ctan/ctan.service.js
+++ b/services/ctan/ctan.service.js
@@ -1,26 +1,23 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderLicenseBadge } from '../licenses.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, InvalidResponse, pathParams } from '../index.js'
const schema = Joi.object({
- license: Joi.array()
- .items(Joi.string())
- .single(),
+ license: Joi.array().items(Joi.string()).single(),
version: Joi.object({
- number: Joi.string().required(),
+ number: Joi.string().allow('').required(),
+ date: Joi.string().allow('').required(),
}).required(),
}).required()
+const description = '[CTAN](https://ctan.org/) is a package registry for TeX.'
+
class BaseCtanService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'ctan' }
- }
+ static defaultBadgeData = { label: 'ctan' }
async fetch({ library }) {
- const url = `http://www.ctan.org/json/pkg/${library}`
+ const url = `https://www.ctan.org/json/2.0/pkg/${library}`
return this._requestJson({
schema,
url,
@@ -29,31 +26,23 @@ class BaseCtanService extends BaseJsonService {
}
class CtanLicense extends BaseCtanService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'ctan/l',
- pattern: ':library',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'CTAN',
- namedParams: { library: 'novel' },
- staticPreview: this.render({ licenses: ['ppl1.3c', 'ofl'] }),
- keywords: ['tex'],
+ static category = 'license'
+ static route = { base: 'ctan/l', pattern: ':library' }
+
+ static openApi = {
+ '/ctan/l/{library}': {
+ get: {
+ summary: 'CTAN License',
+ description,
+ parameters: pathParams({
+ name: 'library',
+ example: 'novel',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ licenses }) {
return renderLicenseBadge({ licenses })
@@ -67,26 +56,20 @@ class CtanLicense extends BaseCtanService {
}
class CtanVersion extends BaseCtanService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'ctan/v',
- pattern: ':library',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'CTAN',
- namedParams: { library: 'tex' },
- staticPreview: this.render({ version: '3.14159265' }),
- keywords: ['tex'],
+ static category = 'version'
+ static route = { base: 'ctan/v', pattern: ':library' }
+
+ static openApi = {
+ '/ctan/v/{library}': {
+ get: {
+ summary: 'CTAN Version',
+ description,
+ parameters: pathParams({
+ name: 'library',
+ example: 'tex',
+ }),
},
- ]
+ },
}
static render({ version }) {
@@ -95,11 +78,23 @@ class CtanVersion extends BaseCtanService {
async handle({ library }) {
const json = await this.fetch({ library })
- return renderVersionBadge({ version: json.version.number })
+ const version = json.version.number
+ if (version !== '') {
+ return renderVersionBadge({ version })
+ } else {
+ const date = json.version.date
+ if (date !== '') {
+ return renderVersionBadge({
+ version: date,
+ versionFormatter: color => 'blue',
+ })
+ } else {
+ return new InvalidResponse({
+ underlyingError: new Error('Both number and date are empty'),
+ })
+ }
+ }
}
}
-module.exports = {
- CtanLicense,
- CtanVersion,
-}
+export { CtanLicense, CtanVersion }
diff --git a/services/ctan/ctan.tester.js b/services/ctan/ctan.tester.js
index 90c558a531255..153be11261516 100644
--- a/services/ctan/ctan.tester.js
+++ b/services/ctan/ctan.tester.js
@@ -1,30 +1,35 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
+// same as isVPlusDottedVersionAtLeastOne, but also accepts an optional
+// single lowercase alphabet letter suffix
+// e.g.: v1.81a
+const isVPlusDottedVersionAtLeastOneWithOptionalAlphabetLetter = withRegex(
+ /^v\d+(\.\d+)?(\.\d+)?[a-z]?$/,
+)
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'ctan',
title: 'Comprehensive TEX Archive Network',
-}))
+})
-t.create('license')
- .get('/l/novel.json')
- .expectBadge({
- label: 'license',
- message: 'lppl1.3c, ofl',
- })
+t.create('license').get('/l/novel.json').expectBadge({
+ label: 'license',
+ message: 'lppl1.3c, ofl',
+})
t.create('license missing')
.get('/l/novel.json')
.intercept(nock =>
- nock('http://www.ctan.org')
- .get('/json/pkg/novel')
+ nock('https://www.ctan.org')
+ .get('/json/2.0/pkg/novel')
.reply(200, {
version: {
number: 'notRelevant',
+ date: 'notRelevant',
},
- })
+ }),
)
.expectBadge({
label: 'license',
@@ -34,40 +39,46 @@ t.create('license missing')
t.create('single license')
.get('/l/tex.json')
.intercept(nock =>
- nock('http://www.ctan.org')
- .get('/json/pkg/tex')
+ nock('https://www.ctan.org')
+ .get('/json/2.0/pkg/tex')
.reply(200, {
license: 'knuth',
version: {
number: 'notRelevant',
+ date: 'notRelevant',
},
- })
+ }),
)
.expectBadge({
label: 'license',
message: 'knuth',
})
-t.create('version')
- .get('/v/novel.json')
- .expectBadge({
- label: 'ctan',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('version').get('/v/novel.json').expectBadge({
+ label: 'ctan',
+ message: isVPlusDottedVersionAtLeastOneWithOptionalAlphabetLetter,
+})
t.create('version')
.get('/v/novel.json')
.intercept(nock =>
- nock('http://www.ctan.org')
- .get('/json/pkg/novel')
+ nock('https://www.ctan.org')
+ .get('/json/2.0/pkg/novel')
.reply(200, {
version: {
number: 'v1.11',
+ date: '',
},
- })
+ }),
)
.expectBadge({
label: 'ctan',
message: 'v1.11',
color: 'blue',
})
+
+t.create('date as version').get('/v/l3kernel.json').expectBadge({
+ label: 'ctan',
+ message: Joi.date().iso(),
+ color: 'blue',
+})
diff --git a/services/curseforge/curseforge-base.js b/services/curseforge/curseforge-base.js
new file mode 100644
index 0000000000000..0e767d89c2c45
--- /dev/null
+++ b/services/curseforge/curseforge-base.js
@@ -0,0 +1,59 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ downloadCount: nonNegativeInteger,
+ latestFiles: Joi.array()
+ .items({
+ displayName: Joi.string().required(),
+ gameVersions: Joi.array().items(Joi.string().required()).required(),
+ })
+ .required(),
+ }).required(),
+}).required()
+
+const description = `
+The CurseForge badge requires the Project ID in order access the
+CurseForge API .
+
+The Project ID is different from the URL slug and can be found in the 'About Project' section of your
+CurseForge mod page.
+
+
+`
+
+export default class BaseCurseForgeService extends BaseJsonService {
+ static auth = {
+ passKey: 'curseforge_api_key',
+ authorizedOrigins: ['https://api.curseforge.com'],
+ isRequired: true,
+ }
+
+ async fetchMod({ projectId }) {
+ // Documentation: https://docs.curseforge.com/#get-mod
+ const response = await this._requestJson(
+ this.authHelper.withApiKeyHeader({
+ schema,
+ url: `https://api.curseforge.com/v1/mods/${projectId}`,
+ httpErrors: {
+ 403: 'invalid API key',
+ },
+ }),
+ )
+
+ const latestFiles = response.data.latestFiles
+ const latestFile =
+ latestFiles.length > 0 ? latestFiles[latestFiles.length - 1] : {}
+
+ return {
+ downloads: response.data.downloadCount,
+ version: latestFile?.displayName || 'N/A',
+ gameVersions: latestFile?.gameVersions || ['N/A'],
+ }
+ }
+}
+
+export { BaseCurseForgeService, description }
diff --git a/services/curseforge/curseforge-downloads.service.js b/services/curseforge/curseforge-downloads.service.js
new file mode 100644
index 0000000000000..ef6ecfa521bed
--- /dev/null
+++ b/services/curseforge/curseforge-downloads.service.js
@@ -0,0 +1,32 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import BaseCurseForgeService, { description } from './curseforge-base.js'
+
+export default class CurseForgeDownloads extends BaseCurseForgeService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'curseforge/dt',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/curseforge/dt/{projectId}': {
+ get: {
+ summary: 'CurseForge Downloads',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: '238222',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ projectId }) {
+ const { downloads } = await this.fetchMod({ projectId })
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/curseforge/curseforge-downloads.tester.js b/services/curseforge/curseforge-downloads.tester.js
new file mode 100644
index 0000000000000..456c001859489
--- /dev/null
+++ b/services/curseforge/curseforge-downloads.tester.js
@@ -0,0 +1,22 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+import { noToken } from '../test-helpers.js'
+import CurseForgeDownloads from './curseforge-downloads.service.js'
+
+export const t = await createServiceTester()
+const noApiKey = noToken(CurseForgeDownloads)
+
+t.create('Downloads')
+ .skipWhen(noApiKey)
+ .get('/238222.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('Downloads (empty)')
+ .skipWhen(noApiKey)
+ .get('/872620.json')
+ .expectBadge({ label: 'downloads', message: '0' })
+
+t.create('Downloads (not found)')
+ .skipWhen(noApiKey)
+ .get('/invalid-project-id.json')
+ .expectBadge({ label: 'downloads', message: 'not found', color: 'red' })
diff --git a/services/curseforge/curseforge-game-versions.service.js b/services/curseforge/curseforge-game-versions.service.js
new file mode 100644
index 0000000000000..4bbc3589ebe8b
--- /dev/null
+++ b/services/curseforge/curseforge-game-versions.service.js
@@ -0,0 +1,39 @@
+import { pathParams } from '../index.js'
+import BaseCurseForgeService, { description } from './curseforge-base.js'
+
+export default class CurseForgeGameVersions extends BaseCurseForgeService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'curseforge/game-versions',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/curseforge/game-versions/{projectId}': {
+ get: {
+ summary: 'CurseForge Game Versions',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: '238222',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'game versions' }
+
+ static render({ versions }) {
+ return {
+ message: versions.join(' | '),
+ color: 'blue',
+ }
+ }
+
+ async handle({ projectId }) {
+ const { gameVersions } = await this.fetchMod({ projectId })
+ const versions = gameVersions
+ return this.constructor.render({ versions })
+ }
+}
diff --git a/services/curseforge/curseforge-game-versions.tester.js b/services/curseforge/curseforge-game-versions.tester.js
new file mode 100644
index 0000000000000..bfbca48b7cd3a
--- /dev/null
+++ b/services/curseforge/curseforge-game-versions.tester.js
@@ -0,0 +1,22 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+import { noToken } from '../test-helpers.js'
+import CurseForgeGameVersions from './curseforge-game-versions.service.js'
+
+export const t = await createServiceTester()
+const noApiKey = noToken(CurseForgeGameVersions)
+
+t.create('Game Versions')
+ .skipWhen(noApiKey)
+ .get('/238222.json')
+ .expectBadge({ label: 'game versions', message: withRegex(/.+( \| )?/) })
+
+t.create('Game Versions (empty)')
+ .skipWhen(noApiKey)
+ .get('/872620.json')
+ .expectBadge({ label: 'game versions', message: 'N/A', color: 'blue' })
+
+t.create('Game Versions (not found)')
+ .skipWhen(noApiKey)
+ .get('/invalid-project-id.json')
+ .expectBadge({ label: 'game versions', message: 'not found', color: 'red' })
diff --git a/services/curseforge/curseforge-version.service.js b/services/curseforge/curseforge-version.service.js
new file mode 100644
index 0000000000000..4babe314f657b
--- /dev/null
+++ b/services/curseforge/curseforge-version.service.js
@@ -0,0 +1,32 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import BaseCurseForgeService, { description } from './curseforge-base.js'
+
+export default class CurseForgeVersion extends BaseCurseForgeService {
+ static category = 'version'
+
+ static route = {
+ base: 'curseforge/v',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/curseforge/v/{projectId}': {
+ get: {
+ summary: 'CurseForge Version',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: '238222',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'version' }
+
+ async handle({ projectId }) {
+ const { version } = await this.fetchMod({ projectId })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/curseforge/curseforge-version.tester.js b/services/curseforge/curseforge-version.tester.js
new file mode 100644
index 0000000000000..9d8a99bd2f07e
--- /dev/null
+++ b/services/curseforge/curseforge-version.tester.js
@@ -0,0 +1,22 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+import { noToken } from '../test-helpers.js'
+import CurseForgeVersion from './curseforge-version.service.js'
+
+export const t = await createServiceTester()
+const noApiKey = noToken(CurseForgeVersion)
+
+t.create('Version')
+ .skipWhen(noApiKey)
+ .get('/238222.json')
+ .expectBadge({ label: 'version', message: withRegex(/.+/) })
+
+t.create('Version (empty)')
+ .skipWhen(noApiKey)
+ .get('/872620.json')
+ .expectBadge({ label: 'version', message: 'N/A', color: 'blue' })
+
+t.create('Version (not found)')
+ .skipWhen(noApiKey)
+ .get('/invalid-project-id.json')
+ .expectBadge({ label: 'version', message: 'not found', color: 'red' })
diff --git a/services/date.js b/services/date.js
new file mode 100644
index 0000000000000..f727c773d5cf6
--- /dev/null
+++ b/services/date.js
@@ -0,0 +1,108 @@
+/**
+ * Commonly-used functions for rendering badges containing a date
+ *
+ * @module
+ */
+
+import dayjs from 'dayjs'
+import calendar from 'dayjs/plugin/calendar.js'
+import customParseFormat from 'dayjs/plugin/customParseFormat.js'
+import { colorScale } from './color-formatters.js'
+import { InvalidResponse } from './index.js'
+
+dayjs.extend(calendar)
+dayjs.extend(customParseFormat)
+
+/**
+ * Parse and validate a string date into a dayjs object. Use this helper
+ * in preference to invoking dayjs directly when parsing a date from string.
+ *
+ * @param {...any} args - Variadic: Arguments to pass through to dayjs
+ * @returns {dayjs} - Parsed object
+ * @throws {InvalidResponse} - Error if validation fails
+ * @see https://day.js.org/docs/en/parse/string
+ * @see https://day.js.org/docs/en/parse/string-format
+ * @see https://day.js.org/docs/en/parse/is-valid
+ * @example
+ * parseDate('2024-01-01')
+ * parseDate('31/01/2024', 'DD/MM/YYYY')
+ * parseDate('2018 Enero 15', 'YYYY MMMM DD', 'es')
+ */
+function parseDate(...args) {
+ let date
+ if (args.length >= 2) {
+ // always use strict mode if format arg is supplied
+ date = dayjs(...args, true)
+ } else {
+ date = dayjs(...args)
+ }
+ if (!date.isValid()) {
+ throw new InvalidResponse({ prettyMessage: 'invalid date' })
+ }
+ return date
+}
+
+/**
+ * Returns a formatted date string without the year based on the value of input date param d.
+ *
+ * @param {Date | string | number | dayjs } d JS Date object, string, unix timestamp or dayjs object
+ * @returns {string} Formatted date string
+ */
+function formatDate(d) {
+ const date = parseDate(d)
+ const dateString = date.calendar(null, {
+ lastDay: '[yesterday]',
+ sameDay: '[today]',
+ lastWeek: '[last] dddd',
+ sameElse: 'MMMM YYYY',
+ })
+ // Trim current year from date string
+ return dateString.replace(` ${dayjs().year()}`, '').toLowerCase()
+}
+
+/**
+ * Determines the color used for a badge according to the age.
+ * Age is calculated as days elapsed till current date.
+ * The color varies from bright green to red as the age increases
+ * or the other way around if `reverse` is given `true`.
+ *
+ * @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
+ * @param {boolean} reversed Reverse the color scale (the older, the better)
+ * @returns {string} Badge color
+ */
+function age(date, reversed = false) {
+ const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, !reversed)
+ const daysElapsed = dayjs().diff(parseDate(date), 'days')
+ return colorByAge(daysElapsed)
+}
+
+/**
+ * Creates a badge object that displays a date
+ *
+ * @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
+ * @param {boolean} reversed Reverse the color scale (the older, the better)
+ * @returns {object} A badge object that has two properties: message, and color
+ */
+function renderDateBadge(date, reversed = false) {
+ const d = parseDate(date)
+ const color = age(d, reversed)
+ const message = formatDate(d)
+ return { message, color }
+}
+
+/**
+ * Returns a relative date from the input timestamp.
+ * For example, day after tomorrow's timestamp will return 'in 2 days'.
+ *
+ * @param {number | string} timestamp - Unix timestamp
+ * @returns {string} Relative date from the unix timestamp
+ */
+function formatRelativeDate(timestamp) {
+ const parsedDate = dayjs.unix(parseInt(timestamp, 10))
+ if (!parsedDate.isValid()) {
+ return 'invalid date'
+ }
+ return dayjs().to(parsedDate).toLowerCase()
+}
+
+export { parseDate, renderDateBadge, formatDate, formatRelativeDate, age }
diff --git a/services/date.spec.js b/services/date.spec.js
new file mode 100644
index 0000000000000..fc74268b3c692
--- /dev/null
+++ b/services/date.spec.js
@@ -0,0 +1,132 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import sinon from 'sinon'
+import { parseDate, formatDate, formatRelativeDate, age } from './date.js'
+import { InvalidResponse } from './index.js'
+
+describe('parseDate', function () {
+ it('parses valid inputs', function () {
+ expect(parseDate('2024-01-01').valueOf()).to.equal(
+ new Date('2024-01-01').valueOf(),
+ )
+ expect(parseDate('Jan 01 01:00:00 2024 GMT').valueOf()).to.equal(
+ new Date('2024-01-01T01:00:00.000Z').valueOf(),
+ )
+ expect(parseDate('31/01/2024', 'DD/MM/YYYY').valueOf()).to.equal(
+ new Date('2024-01-31T00:00:00.000Z').valueOf(),
+ )
+ })
+
+ it('throws when given invalid inputs', function () {
+ // not a date
+ expect(() => parseDate('foo')).to.throw(InvalidResponse)
+ expect(() => parseDate([])).to.throw(InvalidResponse)
+ expect(() => parseDate(null)).to.throw(InvalidResponse)
+
+ // invalid dates (only works with format string)
+ expect(() => parseDate('2024-02-31', 'YYYY-MM-DD')).to.throw(
+ InvalidResponse,
+ )
+ expect(() => parseDate('2024-12-32', 'YYYY-MM-DD')).to.throw(
+ InvalidResponse,
+ )
+
+ // non-standard format with no format string
+ expect(() => parseDate('31/01/2024')).to.throw(InvalidResponse)
+
+ // parse format doesn't match date
+ expect(() => parseDate('2024-01-01', 'YYYYMMDDHHmmss')).to.throw(
+ InvalidResponse,
+ )
+ })
+
+ test(formatDate, () => {
+ given(1465513200000)
+ .describe('when given a timestamp in june 2016')
+ .expect('june 2016')
+ })
+
+ context('in october', function () {
+ let clock
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(new Date(2017, 9, 15).getTime())
+ })
+ afterEach(function () {
+ clock.restore()
+ })
+
+ test(formatDate, () => {
+ given(new Date(2017, 0, 1).getTime())
+ .describe('when given the beginning of this year')
+ .expect('january')
+ })
+ })
+
+ context('in october', function () {
+ let clock
+ beforeEach(function () {
+ clock = sinon.useFakeTimers(new Date(2018, 9, 29).getTime())
+ })
+ afterEach(function () {
+ clock.restore()
+ })
+
+ test(formatRelativeDate, () => {
+ given(new Date(2018, 9, 31).getTime() / 1000)
+ .describe('when given the end of october')
+ .expect('in 2 days')
+ })
+
+ test(formatRelativeDate, () => {
+ given(new Date(2018, 9, 1).getTime() / 1000)
+ .describe('when given the beginning of october')
+ .expect('a month ago')
+ })
+
+ test(formatRelativeDate, () => {
+ given(9999999999999)
+ .describe('when given invalid date')
+ .expect('invalid date')
+ })
+ })
+
+ const monthsAgo = months => {
+ const result = new Date()
+ // This looks wack but it works.
+ result.setMonth(result.getMonth() - months)
+ return result
+ }
+ test(age, () => {
+ given(Date.now())
+ .describe('when given the current timestamp')
+ .expect('brightgreen')
+ given(new Date())
+ .describe('when given the current Date')
+ .expect('brightgreen')
+ given(new Date(2001, 1, 1))
+ .describe('when given a Date many years ago')
+ .expect('red')
+ given(monthsAgo(2))
+ .describe('when given a Date two months ago')
+ .expect('yellowgreen')
+ given(monthsAgo(15))
+ .describe('when given a Date 15 months ago')
+ .expect('orange')
+ // --- reversed --- //
+ given(Date.now(), true)
+ .describe('when given the current timestamp and reversed')
+ .expect('red')
+ given(new Date(), true)
+ .describe('when given the current Date and reversed')
+ .expect('red')
+ given(new Date(2001, 1, 1), true)
+ .describe('when given a Date many years ago and reversed')
+ .expect('brightgreen')
+ given(monthsAgo(2), true)
+ .describe('when given a Date two months ago and reversed')
+ .expect('yellow')
+ given(monthsAgo(15), true)
+ .describe('when given a Date 15 months ago and reversed')
+ .expect('green')
+ })
+})
diff --git a/services/date/date.service.js b/services/date/date.service.js
index d86f61c4093a5..11e6fa3cb2dd3 100644
--- a/services/date/date.service.js
+++ b/services/date/date.service.js
@@ -1,42 +1,28 @@
-'use strict'
+import { formatRelativeDate } from '../date.js'
+import { BaseService, pathParams } from '../index.js'
-const { formatRelativeDate } = require('../text-formatters')
-const { BaseService } = require('..')
-
-const documentation = `
-
- Supply a unix timestamp in seconds to display the relative time from/to now
-
+const description = `
+Supply a unix timestamp in seconds to display the relative time from/to now
`
-module.exports = class Date extends BaseService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'date',
- pattern: ':timestamp([0-9]+)',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Relative date',
- pattern: ':timestamp',
- namedParams: { timestamp: '1540814400' },
- staticPreview: this.render({ relativeDateString: '2 days ago' }),
- keywords: ['time', 'countdown', 'countup', 'moment'],
- documentation,
+export default class Date extends BaseService {
+ static category = 'other'
+ static route = { base: 'date', pattern: ':timestamp(-?[0-9]+)' }
+
+ static openApi = {
+ '/date/{timestamp}': {
+ get: {
+ summary: 'Relative date',
+ description,
+ parameters: pathParams({
+ name: 'timestamp',
+ example: '1540814400',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'date' }
- }
+ static defaultBadgeData = { label: 'date' }
static render({ relativeDateString }) {
return {
diff --git a/services/date/date.tester.js b/services/date/date.tester.js
index 3e0135bd01d53..423cdc601394f 100644
--- a/services/date/date.tester.js
+++ b/services/date/date.tester.js
@@ -1,17 +1,19 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isRelativeFormattedDate } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isRelativeFormattedDate } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'date',
title: 'Relative Date Tests',
-}))
+})
t.create('Relative date')
.get('/1540814400.json')
.expectBadge({ label: 'date', message: isRelativeFormattedDate })
+t.create('Relative date (negative)')
+ .get('/-1.json')
+ .expectBadge({ label: 'date', message: isRelativeFormattedDate })
+
t.create('Relative date - Invalid')
.get('/9999999999999.json')
.expectBadge({ label: 'date', message: 'invalid date' })
diff --git a/services/david/david.service.js b/services/david/david.service.js
deleted file mode 100644
index a48874ea95fd4..0000000000000
--- a/services/david/david.service.js
+++ /dev/null
@@ -1,109 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- status: Joi.allow(
- 'insecure',
- 'outofdate',
- 'notsouptodate',
- 'uptodate',
- 'none'
- ).required(),
-}).required()
-
-const queryParamSchema = Joi.object({
- path: Joi.string(),
-}).required()
-
-const statusMap = {
- insecure: {
- color: 'red',
- message: 'insecure',
- },
- outofdate: {
- color: 'red',
- message: 'out of date',
- },
- notsouptodate: {
- color: 'yellow',
- message: 'up to date',
- },
- uptodate: {
- color: 'brightgreen',
- message: 'up to date',
- },
- none: {
- color: 'brightgreen',
- message: 'none',
- },
-}
-
-module.exports = class David extends BaseJsonService {
- static get category() {
- return 'dependencies'
- }
-
- static get route() {
- return {
- base: 'david',
- pattern: ':kind(dev|optional|peer)?/:user/:repo',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'David',
- namedParams: { user: 'expressjs', repo: 'express' },
- staticPreview: this.render({ status: 'uptodate' }),
- },
- {
- title: 'David (path)',
- namedParams: { user: 'babel', repo: 'babel' },
- queryParams: { path: 'packages/babel-core' },
- staticPreview: this.render({ status: 'uptodate' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'dependencies' }
- }
-
- static render({ status, kind }) {
- return {
- message: statusMap[status].message,
- color: statusMap[status].color,
- label: `${kind ? `${kind} ` : ''}dependencies`,
- }
- }
-
- async fetch({ kind, user, repo, path }) {
- const url = `https://david-dm.org/${user}/${repo}/${
- kind ? `${kind}-` : ''
- }info.json`
-
- return this._requestJson({
- schema,
- url,
- options: { qs: { path } },
- errorMessages: {
- /* note:
- david returns a 500 response for 'not found'
- e.g: https://david-dm.org/foo/barbaz/info.json
- not a 404 so we can't handle 'not found' cleanly
- because this might also be some other error.
- */
- 500: 'repo or path not found or david internal error',
- },
- })
- }
-
- async handle({ kind, user, repo }, { path }) {
- const json = await this.fetch({ kind, user, repo, path })
- return this.constructor.render({ status: json.status, kind })
- }
-}
diff --git a/services/david/david.tester.js b/services/david/david.tester.js
deleted file mode 100644
index 72f50c5057cb0..0000000000000
--- a/services/david/david.tester.js
+++ /dev/null
@@ -1,63 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isDependencyStatus = Joi.string().valid(
- 'insecure',
- 'up to date',
- 'out of date'
-)
-
-t.create('david dependencies (valid)')
- .get('/expressjs/express.json')
- .expectBadge({
- label: 'dependencies',
- message: isDependencyStatus,
- })
-
-t.create('david dev dependencies (valid)')
- .get('/dev/expressjs/express.json')
- .expectBadge({
- label: 'dev dependencies',
- message: isDependencyStatus,
- })
-
-t.create('david optional dependencies (valid)')
- .get('/optional/elnounch/byebye.json')
- .expectBadge({
- label: 'optional dependencies',
- message: isDependencyStatus,
- })
-
-t.create('david peer dependencies (valid)')
- .get('/peer/webcomponents/generator-element.json')
- .expectBadge({
- label: 'peer dependencies',
- message: isDependencyStatus,
- })
-
-t.create('david dependencies with path (valid)')
- .get('/babel/babel.json?path=packages/babel-core')
- .expectBadge({
- label: 'dependencies',
- message: isDependencyStatus,
- })
-
-t.create('david dependencies (none)')
- .get('/peer/expressjs/express.json') // express does not specify peer dependencies
- .expectBadge({ label: 'peer dependencies', message: 'none' })
-
-t.create('david dependencies (repo not found)')
- .get('/pyvesb/emptyrepo.json')
- .expectBadge({
- label: 'dependencies',
- message: 'repo or path not found or david internal error',
- })
-
-t.create('david dependencies (path not found')
- .get('/babel/babel.json?path=invalid/path')
- .expectBadge({
- label: 'dependencies',
- message: 'repo or path not found or david internal error',
- })
diff --git a/services/debian/debian.service.js b/services/debian/debian.service.js
index a6d306be48c73..0accce7820ce2 100644
--- a/services/debian/debian.service.js
+++ b/services/debian/debian.service.js
@@ -1,8 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { latest, renderVersionBadge } = require('../version')
-const { BaseJsonService, NotFound, InvalidResponse } = require('..')
+import Joi from 'joi'
+import { latest, renderVersionBadge } from '../version.js'
+import {
+ BaseJsonService,
+ NotFound,
+ InvalidResponse,
+ pathParams,
+} from '../index.js'
const schema = Joi.array()
.items(
@@ -10,46 +13,56 @@ const schema = Joi.array()
/./,
Joi.object()
.pattern(/./, Joi.object().pattern(/./, Joi.object()))
- .required()
- ) // Optional, missing means not found
+ .required(),
+ ), // Optional, missing means not found
)
.max(1)
.required()
const defaultDistribution = 'stable'
-module.exports = class Debian extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'debian/v',
- pattern: ':packageName/:distribution?',
- }
+export default class Debian extends BaseJsonService {
+ static category = 'version'
+ static route = {
+ base: 'debian/v',
+ pattern: ':packageName/:distribution?',
}
- static get examples() {
- return [
- {
- title: 'Debian package',
- namedParams: { packageName: 'apt', distribution: 'unstable' },
- staticPreview: renderVersionBadge({ version: '1.8.0' }),
+ static openApi = {
+ '/debian/v/{packageName}/{distribution}': {
+ get: {
+ summary: 'Debian package (for distribution)',
+ parameters: pathParams(
+ {
+ name: 'packageName',
+ example: 'apt',
+ },
+ {
+ name: 'distribution',
+ example: 'unstable',
+ },
+ ),
+ },
+ },
+ '/debian/v/{packageName}': {
+ get: {
+ summary: 'Debian package',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'apt',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'debian' }
- }
+ static defaultBadgeData = { label: 'debian' }
async handle({ packageName, distribution = defaultDistribution }) {
const data = await this._requestJson({
schema,
url: 'https://api.ftp-master.debian.org/madison',
options: {
- qs: {
+ searchParams: {
f: 'json',
s: distribution,
package: packageName,
diff --git a/services/debian/debian.tester.js b/services/debian/debian.tester.js
index 046c31d1e184b..d379c00ff06db 100644
--- a/services/debian/debian.tester.js
+++ b/services/debian/debian.tester.js
@@ -1,9 +1,6 @@
-'use strict'
-
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Debian package (default distribution, valid)')
.get('/apt.json')
@@ -28,7 +25,7 @@ t.create('Debian package (valid)')
{
apt: { unstable: { '1.8.0': { source: 'apt', component: 'main' } } },
},
- ])
+ ]),
)
.expectBadge({ label: 'debian', message: 'v1.8.0' })
@@ -44,7 +41,7 @@ t.create('Debian package (invalid, more than one result)')
{
apt: { unstable: { '1.8.1': { source: 'apt', component: 'main' } } },
},
- ])
+ ]),
)
.expectBadge({ label: 'debian', message: 'invalid response data' })
@@ -59,7 +56,7 @@ t.create('Debian package (invalid, requested package missing from response)')
unstable: { '1.8.0': { source: 'apt', component: 'main' } },
},
},
- ])
+ ]),
)
.expectBadge({ label: 'debian', message: 'invalid response data' })
diff --git a/services/debug/debug.service.js b/services/debug/debug.service.js
index b9ef88ca32677..93dc625deaf3a 100644
--- a/services/debug/debug.service.js
+++ b/services/debug/debug.service.js
@@ -1,28 +1,13 @@
-'use strict'
-
-const { NonMemoryCachingBaseService } = require('..')
+import { BaseService } from '../index.js'
const serverStartTime = new Date(new Date().toGMTString())
let bitFlip = false
-module.exports = class Debug extends NonMemoryCachingBaseService {
- static get category() {
- return 'debug'
- }
+export default class Debug extends BaseService {
+ static category = 'debug'
+ static route = { base: 'debug', pattern: ':variant(time|starttime|flip)' }
- static get route() {
- return {
- base: 'debug',
- pattern: ':variant(time|starttime|flip)',
- }
- }
-
- static get defaultBadgeData() {
- return {
- label: 'debug',
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'debug', color: 'blue' }
async handle({ variant }) {
switch (variant) {
diff --git a/services/debug/debug.tester.js b/services/debug/debug.tester.js
index 745862ad3f97f..f57dcaf34141e 100644
--- a/services/debug/debug.tester.js
+++ b/services/debug/debug.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('start time')
.get('/starttime.json')
diff --git a/services/dependabot/dependabot.service.js b/services/dependabot/dependabot.service.js
deleted file mode 100644
index 17a64824cee45..0000000000000
--- a/services/dependabot/dependabot.service.js
+++ /dev/null
@@ -1,73 +0,0 @@
-'use strict'
-
-const url = require('url')
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- status: Joi.string().required(),
- colour: Joi.string().required(),
-})
-
-module.exports = class DependabotSemverCompatibility extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'dependabot/semver',
- pattern: ':packageManager/:dependencyName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Dependabot SemVer Compatibility',
- namedParams: { packageManager: 'bundler', dependencyName: 'puma' },
- staticPreview: {
- color: 'green',
- message: '98%',
- },
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'semver stability' }
- }
-
- _getQuery({ packageManager, dependencyName }) {
- return {
- 'package-manager': packageManager,
- 'dependency-name': dependencyName,
- 'version-scheme': 'semver',
- }
- }
-
- async fetch({ packageManager, dependencyName }) {
- const url = `https://api.dependabot.com/badges/compatibility_score`
- return this._requestJson({
- schema,
- url,
- options: { qs: this._getQuery({ packageManager, dependencyName }) },
- })
- }
-
- _getLink({ packageManager, dependencyName }) {
- const qs = new url.URLSearchParams(
- this._getQuery({ packageManager, dependencyName })
- )
- return `https://dependabot.com/compatibility-score.html?${qs.toString()}`
- }
-
- async handle({ packageManager, dependencyName }) {
- const json = await this.fetch({ packageManager, dependencyName })
- return {
- color: json.colour,
- message: json.status,
- link: this._getLink({ packageManager, dependencyName }),
- }
- }
-}
diff --git a/services/dependabot/dependabot.tester.js b/services/dependabot/dependabot.tester.js
deleted file mode 100644
index 610e0649a5f38..0000000000000
--- a/services/dependabot/dependabot.tester.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('semver stability (valid)')
- .get('/bundler/puma.json')
- .expectBadge({
- label: 'semver stability',
- message: isIntegerPercentage,
- link: [
- 'https://dependabot.com/compatibility-score.html?package-manager=bundler&dependency-name=puma&version-scheme=semver',
- ],
- })
-
-t.create('semver stability (invalid error)')
- .get('/invalid-manager/puma.json')
- .expectBadge({
- label: 'semver stability',
- message: 'invalid',
- color: 'lightgrey',
- })
-
-t.create('semver stability (missing dependency)')
- .get('/bundler/some-random-missing-dependency.json')
- .expectBadge({
- label: 'semver stability',
- message: 'unknown',
- })
diff --git a/services/depfu/depfu.service.js b/services/depfu/depfu.service.js
index 77fe36b02443c..a1c0dbc2eec93 100644
--- a/services/depfu/depfu.service.js
+++ b/services/depfu/depfu.service.js
@@ -1,41 +1,43 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import {
+ BaseJsonService,
+ InvalidParameter,
+ redirector,
+ pathParams,
+} from '../index.js'
const depfuSchema = Joi.object({
text: Joi.string().required(),
colorscheme: Joi.string().required(),
}).required()
-module.exports = class Depfu extends BaseJsonService {
- static get category() {
- return 'dependencies'
- }
-
- static get route() {
- return {
- base: 'depfu',
- pattern: ':user/:repo',
- }
+class Depfu extends BaseJsonService {
+ static category = 'dependencies'
+ static route = {
+ base: 'depfu/dependencies',
+ pattern: ':vcsType(github|gitlab)/:project+',
}
- static get examples() {
- return [
- {
- title: 'Depfu',
- namedParams: { user: 'depfu', repo: 'example-ruby' },
- staticPreview: this.render({
- text: 'recent',
- colorscheme: 'brightgreen',
- }),
+ static openApi = {
+ '/depfu/dependencies/{vcsType}/{project}': {
+ get: {
+ summary: 'Depfu',
+ parameters: pathParams(
+ {
+ name: 'vcsType',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('vcsType') },
+ },
+ {
+ name: 'project',
+ example: 'depfu/example-ruby',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'dependencies' }
- }
+ static defaultBadgeData = { label: 'dependencies' }
static render({ text, colorscheme }) {
return {
@@ -44,13 +46,31 @@ module.exports = class Depfu extends BaseJsonService {
}
}
- async fetch({ user, repo }) {
- const url = `https://depfu.com/github/shields/${user}/${repo}`
+ async fetch({ vcsType, project }) {
+ const separatorPosition = project.lastIndexOf('/')
+ if (separatorPosition < 0) {
+ throw new InvalidParameter()
+ }
+ const user = encodeURIComponent(project.substr(0, separatorPosition))
+ const repo = project.substr(separatorPosition)
+ const url = `https://depfu.com/${vcsType}/shields/${user}/${repo}`
return this._requestJson({ url, schema: depfuSchema })
}
- async handle({ user, repo }) {
- const { text, colorscheme } = await this.fetch({ user, repo })
+ async handle({ vcsType, project }) {
+ const { text, colorscheme } = await this.fetch({ vcsType, project })
return this.constructor.render({ text, colorscheme })
}
}
+
+const legacyRoutes = [
+ redirector({
+ category: 'dependencies',
+ route: { base: 'depfu', pattern: ':user/:repo' },
+ transformPath: ({ user, repo }) =>
+ `/depfu/dependencies/github/${user}/${repo}`,
+ dateAdded: new Date('2022-01-11'),
+ }),
+]
+
+export default { ...legacyRoutes, Depfu }
diff --git a/services/depfu/depfu.tester.js b/services/depfu/depfu.tester.js
index d8fea928d4037..17e6c829cf477 100644
--- a/services/depfu/depfu.tester.js
+++ b/services/depfu/depfu.tester.js
@@ -1,24 +1,60 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
const isDependencyStatus = Joi.string().valid(
'insecure',
'latest',
'recent',
- 'stale'
+ 'stale',
)
-const t = (module.exports = new ServiceTester({ id: 'depfu', title: 'Depfu' }))
+export const t = new ServiceTester({ id: 'depfu', title: 'Depfu' })
+
+t.create('depfu Github dependencies (valid)')
+ .get('/dependencies/github/depfu/example-ruby.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: isDependencyStatus,
+ })
+
+t.create('depfu Github dependencies (repo not found)')
+ .get('/dependencies/github/pyvesb/emptyrepo.json')
+ .expectBadge({ label: 'dependencies', message: 'not found' })
+
+t.create('depfu Gitlab dependencies (valid)')
+ .get('/dependencies/gitlab/depfu/example-ruby.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: isDependencyStatus,
+ })
+
+t.create('depfu Github dependencies (no separator)')
+ .get('/dependencies/github/example-ruby.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: 'invalid parameter',
+ })
-t.create('depfu dependencies (valid)')
- .get('/depfu/example-ruby.json')
+t.create('depfu Gitlab dependencies (valid with subgroup)')
+ .get(
+ '/dependencies/gitlab/shields-example-group/subgroup/example-nodejs.json',
+ )
.expectBadge({
label: 'dependencies',
message: isDependencyStatus,
})
-t.create('depfu dependencies (repo not found)')
- .get('/pyvesb/emptyrepo.json')
+t.create('depfu Gitlab dependencies (repo not found)')
+ .get('/dependencies/gitlab/fdroid/nonexistant.json')
.expectBadge({ label: 'dependencies', message: 'not found' })
+
+t.create('depfu Gitlab dependencies (no separator)')
+ .get('/dependencies/gitlab/example-ruby.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: 'invalid parameter',
+ })
+
+t.create('legacy route (assume "github" as a default VCS)')
+ .get('/depfu/example-ruby.svg')
+ .expectRedirect('/depfu/dependencies/github/depfu/example-ruby.svg')
diff --git a/services/deprecation-helpers.js b/services/deprecation-helpers.js
deleted file mode 100644
index d01055bd004d8..0000000000000
--- a/services/deprecation-helpers.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { Deprecated } = require('.')
-
-function enforceDeprecation(effectiveDate) {
- if (Date.now() >= effectiveDate.getTime()) {
- throw new Deprecated()
- }
-}
-
-module.exports = {
- enforceDeprecation,
-}
diff --git a/services/deprecation-helpers.spec.js b/services/deprecation-helpers.spec.js
deleted file mode 100644
index 26bc307f9d7f5..0000000000000
--- a/services/deprecation-helpers.spec.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const { Deprecated } = require('../core/base-service/errors')
-const { enforceDeprecation } = require('./deprecation-helpers')
-
-describe('enforceDeprecation', function() {
- it('throws Deprecated for a date in the past', function() {
- expect(() => enforceDeprecation(new Date())).to.throw(Deprecated)
- })
-
- it('does not throw for a date in the future', function() {
- expect(() =>
- enforceDeprecation(new Date(Date.now() + 10000))
- ).not.to.throw()
- })
-})
diff --git a/services/deps-rs/deps-rs-base.js b/services/deps-rs/deps-rs-base.js
new file mode 100644
index 0000000000000..2e8acd8c9574c
--- /dev/null
+++ b/services/deps-rs/deps-rs-base.js
@@ -0,0 +1,85 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+
+const depsResponseSchema = Joi.object({
+ message: Joi.string().required(),
+}).required()
+
+class BaseDepsRsService extends BaseJsonService {
+ static defaultBadgeData = { label: 'dependencies' }
+
+ /**
+ * Maps message values to shields color scheme
+ *
+ * @param {string} message - The message to help determine appropriate color
+ * @returns {string} The shields color
+ */
+ static mapColor(message) {
+ const normalizedMessage = message.toLowerCase()
+
+ const colorMap = {
+ 'up to date': 'brightgreen',
+ none: 'brightgreen',
+ 'maybe insecure': 'yellowgreen',
+ insecure: 'red',
+ unknown: 'lightgrey',
+ 'not found': 'lightgrey',
+ invalid: 'lightgrey',
+ }
+
+ if (colorMap[normalizedMessage]) {
+ return colorMap[normalizedMessage]
+ }
+
+ // e.g. "1 of 2 outdated"
+ if (normalizedMessage.endsWith('outdated')) {
+ return 'yellow'
+ }
+
+ return 'lightgrey'
+ }
+
+ /**
+ * Fetches data from the deps.rs API.
+ *
+ * @param {object} options - The options for the request
+ * @param {string} options.crate - The crate name.
+ * @param {string} options.version - The crate version number or 'latest'.
+ * @returns {Promise} the JSON response from the API.
+ */
+ async fetchCrate({ crate, version }) {
+ const url = `https://deps.rs/crate/${crate}/${version}/shield.json`
+ return this._requestJson({
+ schema: depsResponseSchema,
+ url,
+ httpErrors: {
+ 404: 'not found',
+ },
+ })
+ }
+
+ /**
+ * Fetches data from the deps.rs API for a repository.
+ *
+ * @param {object} options - The options for the request
+ * @param {string} options.site - The repository site (e.g., 'github').
+ * @param {string} options.user - The repository owner/user.
+ * @param {string} options.repo - The repository name.
+ * @returns {Promise} the JSON response from the API.
+ */
+ async fetchRepo({ site, user, repo }) {
+ const url = `https://deps.rs/repo/${site}/${user}/${repo}/shield.json`
+ return this._requestJson({
+ schema: depsResponseSchema,
+ url,
+ httpErrors: {
+ 404: 'unknown',
+ },
+ })
+ }
+}
+
+const description =
+ '[Deps.rs](https://deps.rs/) is a service that checks dependency security status in Rust projects.'
+
+export { BaseDepsRsService, description }
diff --git a/services/deps-rs/deps-rs-crate.service.js b/services/deps-rs/deps-rs-crate.service.js
new file mode 100644
index 0000000000000..ea955c65effbd
--- /dev/null
+++ b/services/deps-rs/deps-rs-crate.service.js
@@ -0,0 +1,47 @@
+import { pathParams } from '../index.js'
+import { BaseDepsRsService, description } from './deps-rs-base.js'
+
+export default class DepsRsCrate extends BaseDepsRsService {
+ static category = 'dependencies'
+ static route = { base: 'deps-rs', pattern: ':crate/:version' }
+
+ static openApi = {
+ '/deps-rs/{crate}/latest': {
+ get: {
+ summary: 'Deps.rs Crate Dependencies (latest)',
+ description,
+ parameters: pathParams({
+ name: 'crate',
+ example: 'syn',
+ description: 'The name of the Rust crate',
+ }),
+ },
+ },
+ '/deps-rs/{crate}/{version}': {
+ get: {
+ summary: 'Deps.rs Crate Dependencies (specific version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'syn',
+ description: 'The name of the Rust crate',
+ },
+ {
+ name: 'version',
+ example: '2.0.101',
+ description: 'Version number',
+ },
+ ),
+ },
+ },
+ }
+
+ async handle({ crate, version }) {
+ const json = await this.fetchCrate({ crate, version })
+ return {
+ message: json.message,
+ color: this.constructor.mapColor(json.message),
+ }
+ }
+}
diff --git a/services/deps-rs/deps-rs-crate.tester.js b/services/deps-rs/deps-rs-crate.tester.js
new file mode 100644
index 0000000000000..f2c2d2f01a38b
--- /dev/null
+++ b/services/deps-rs/deps-rs-crate.tester.js
@@ -0,0 +1,29 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+// This validator is only for cases where the crate is known to exist.
+// It does not include 'unknown' or 'not found', as these test cases always use existing crates.
+// For non-existent crates, use a separate validator in the relevant test case.
+const validCrateMessageValidator = Joi.alternatives().try(
+ Joi.valid('up to date', 'none', 'maybe insecure', 'insecure'),
+ Joi.string().pattern(/outdated$/),
+)
+
+t.create('dependencies (valid crate, latest version)')
+ .get('/syn/latest.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: validCrateMessageValidator,
+ })
+
+t.create('dependencies (valid crate with specific version)')
+ .get('/syn/2.0.101.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: validCrateMessageValidator,
+ })
+
+t.create('dependencies (not found)')
+ .get('/not-a-real-package/latest.json')
+ .expectBadge({ label: 'dependencies', message: 'not found' })
diff --git a/services/deps-rs/deps-rs-repo.service.js b/services/deps-rs/deps-rs-repo.service.js
new file mode 100644
index 0000000000000..e43a38b0c6902
--- /dev/null
+++ b/services/deps-rs/deps-rs-repo.service.js
@@ -0,0 +1,42 @@
+import { pathParams } from '../index.js'
+import { BaseDepsRsService, description } from './deps-rs-base.js'
+
+export default class DepsRsRepo extends BaseDepsRsService {
+ static category = 'dependencies'
+ static route = {
+ base: 'deps-rs/repo',
+ pattern: ':site(github|gitlab|bitbucket|sourcehut|codeberg)/:user/:repo',
+ }
+
+ static openApi = {
+ '/deps-rs/repo/{site}/{user}/{repo}': {
+ get: {
+ summary: 'Deps.rs Repository Dependencies',
+ description,
+ parameters: pathParams(
+ {
+ name: 'site',
+ example: 'github',
+ schema: { type: 'string', enum: this.getEnum('site') },
+ },
+ {
+ name: 'user',
+ example: 'dtolnay',
+ },
+ {
+ name: 'repo',
+ example: 'syn',
+ },
+ ),
+ },
+ },
+ }
+
+ async handle({ site, user, repo }) {
+ const json = await this.fetchRepo({ site, user, repo })
+ return {
+ message: json.message,
+ color: this.constructor.mapColor(json.message),
+ }
+ }
+}
diff --git a/services/deps-rs/deps-rs-repo.tester.js b/services/deps-rs/deps-rs-repo.tester.js
new file mode 100644
index 0000000000000..7b428c45f97f3
--- /dev/null
+++ b/services/deps-rs/deps-rs-repo.tester.js
@@ -0,0 +1,22 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+// This validator is only for cases where the repository is known to exist.
+// It does not include 'unknown' or 'not found', as these test cases always use existing repositories.
+// For non-existent or unknown repositories, use a separate validator in the relevant test case.
+const validRepoMessageValidator = Joi.alternatives().try(
+ Joi.valid('up to date', 'none', 'maybe insecure', 'insecure'),
+ Joi.string().pattern(/outdated$/),
+)
+
+t.create('dependencies (valid repo)')
+ .get('/github/dtolnay/syn.json')
+ .expectBadge({
+ label: 'dependencies',
+ message: validRepoMessageValidator,
+ })
+
+t.create('dependencies (unknown)')
+ .get('/github/not-a-real-user/not-a-real-repo.json')
+ .expectBadge({ label: 'dependencies', message: 'unknown' })
diff --git a/services/discord/discord.service.js b/services/discord/discord.service.js
index 8a3d418577afa..d21a9d6411a04 100644
--- a/services/discord/discord.service.js
+++ b/services/discord/discord.service.js
@@ -1,102 +1,82 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const discordSchema = Joi.object({
+const schema = Joi.object({
presence_count: nonNegativeInteger,
}).required()
-const proxySchema = Joi.object({
- message: Joi.string().required(),
- color: Joi.string().required(),
-}).required()
+const description = `
+The Discord badge requires the SERVER ID in order access the Discord JSON API.
+
+The SERVER ID can be located in the url of the channel that the badge is accessing.
-const documentation = `
-
- The Discord badge requires the SERVER ID in order access the Discord JSON API.
-
-
- The SERVER ID can be located in the url of the channel that the badge is accessing.
-
-
- To use the Discord badge a Discord server admin must enable the widget setting on the server.
-
-
+
+To use the Discord badge a Discord server admin must enable the widget setting on the server.
+
+
`
-module.exports = class Discord extends BaseJsonService {
- static get category() {
- return 'chat'
+export default class Discord extends BaseJsonService {
+ static category = 'chat'
+
+ static route = {
+ base: 'discord',
+ pattern: ':serverId',
}
- static get route() {
- return {
- base: 'discord',
- pattern: ':serverId',
- }
+ static auth = {
+ passKey: 'discord_bot_token',
+ authorizedOrigins: ['https://discord.com'],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Discord',
- namedParams: { serverId: '102860784329052160' },
- staticPreview: this.render({ members: 23 }),
- documentation,
+ static openApi = {
+ '/discord/{serverId}': {
+ get: {
+ summary: 'Discord',
+ description,
+ parameters: pathParams({
+ name: 'serverId',
+ example: '308323056592486420',
+ }),
},
- ]
+ },
}
- static get _cacheLength() {
- return 30
- }
+ static _cacheLength = 300
- static get defaultBadgeData() {
- return { label: 'chat' }
- }
+ static defaultBadgeData = { label: 'chat' }
static render({ members }) {
return {
- message: `${members} online`,
+ message: `${metric(members)} online`,
color: 'brightgreen',
}
}
- constructor(context, config) {
- super(context, config)
- this._shieldsProductionHerokuHacks = config.shieldsProductionHerokuHacks
- }
-
async fetch({ serverId }) {
- const url = `https://discordapp.com/api/guilds/${serverId}/widget.json`
- return this._requestJson({
- url,
- schema: discordSchema,
- errorMessages: {
- 404: 'invalid server',
- 403: 'widget disabled',
- },
- })
- }
-
- async fetchOvhProxy({ serverId }) {
- return this._requestJson({
- url: `https://legacy-img.shields.io/discord/${serverId}.json`,
- schema: proxySchema,
- })
+ const url = `https://discord.com/api/v6/guilds/${serverId}/widget.json`
+ return this._requestJson(
+ this.authHelper.withBearerAuthHeader(
+ {
+ url,
+ schema,
+ httpErrors: {
+ 404: 'invalid server',
+ 403: 'widget disabled',
+ },
+ },
+ 'Bot',
+ ),
+ )
}
async handle({ serverId }) {
- if (this._shieldsProductionHerokuHacks) {
- const { message, color } = await this.fetchOvhProxy({ serverId })
- return { message, color }
- }
-
const data = await this.fetch({ serverId })
return this.constructor.render({ members: data.presence_count })
}
diff --git a/services/discord/discord.spec.js b/services/discord/discord.spec.js
new file mode 100644
index 0000000000000..4da1d14de5720
--- /dev/null
+++ b/services/discord/discord.spec.js
@@ -0,0 +1,15 @@
+import { testAuth } from '../test-helpers.js'
+import Discord from './discord.service.js'
+
+describe('Discord', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ Discord,
+ 'BearerAuthHeader',
+ { presence_count: 125 },
+ { bearerHeaderKey: 'Bot' },
+ )
+ })
+ })
+})
diff --git a/services/discord/discord.tester.js b/services/discord/discord.tester.js
index 86517e1a40a7d..68bd780f2c7ee 100644
--- a/services/discord/discord.tester.js
+++ b/services/discord/discord.tester.js
@@ -1,13 +1,13 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isMetricWithPattern } from '../test-validators.js'
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = await createServiceTester()
-t.create('gets status for Reactiflux')
- .get('/102860784329052160.json')
+t.create('gets status for shields')
+ .get('/308323056592486420.json')
.expectBadge({
label: 'chat',
- message: Joi.string().regex(/^[0-9]+ online$/),
+ message: isMetricWithPattern(/ online/),
color: 'brightgreen',
})
@@ -18,20 +18,20 @@ t.create('invalid server ID')
t.create('widget disabled')
.get('/12345.json')
.intercept(nock =>
- nock('https://discordapp.com/')
- .get('/api/guilds/12345/widget.json')
+ nock('https://discord.com/')
+ .get('/api/v6/guilds/12345/widget.json')
.reply(403, {
code: 50004,
message: 'Widget Disabled',
- })
+ }),
)
.expectBadge({ label: 'chat', message: 'widget disabled' })
t.create('server error')
.get('/12345.json')
.intercept(nock =>
- nock('https://discordapp.com/')
- .get('/api/guilds/12345/widget.json')
- .reply(500, 'Something broke')
+ nock('https://discord.com/')
+ .get('/api/v6/guilds/12345/widget.json')
+ .reply(500, 'Something broke'),
)
.expectBadge({ label: 'chat', message: 'inaccessible' })
diff --git a/services/discourse/discourse-redirect.service.js b/services/discourse/discourse-redirect.service.js
index 72d265fe109e7..f1285eba110b8 100644
--- a/services/discourse/discourse-redirect.service.js
+++ b/services/discourse/discourse-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
redirector({
category: 'chat',
route: {
diff --git a/services/discourse/discourse-redirect.tester.js b/services/discourse/discourse-redirect.tester.js
index 772eb3465873a..84e15b0565b2d 100644
--- a/services/discourse/discourse-redirect.tester.js
+++ b/services/discourse/discourse-redirect.tester.js
@@ -1,49 +1,47 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'DiscourseRedirect',
title: 'DiscourseRedirect',
pathPrefix: '/discourse',
-}))
+})
t.create('discourse status')
.get('/https/meta.discourse.org/status.svg')
.expectRedirect(
`/discourse/status.svg?server=${encodeURIComponent(
- 'https://meta.discourse.org'
- )}`
+ 'https://meta.discourse.org',
+ )}`,
)
t.create('discourse topics')
.get('/https/meta.discourse.org/topics.svg')
.expectRedirect(
`/discourse/topics.svg?server=${encodeURIComponent(
- 'https://meta.discourse.org'
- )}`
+ 'https://meta.discourse.org',
+ )}`,
)
t.create('discourse users')
.get('/https/meta.discourse.org/users.svg')
.expectRedirect(
`/discourse/users.svg?server=${encodeURIComponent(
- 'https://meta.discourse.org'
- )}`
+ 'https://meta.discourse.org',
+ )}`,
)
t.create('discourse likes')
.get('/https/meta.discourse.org/likes.svg')
.expectRedirect(
`/discourse/likes.svg?server=${encodeURIComponent(
- 'https://meta.discourse.org'
- )}`
+ 'https://meta.discourse.org',
+ )}`,
)
t.create('discourse posts')
.get('/https/meta.discourse.org/posts.svg')
.expectRedirect(
`/discourse/posts.svg?server=${encodeURIComponent(
- 'https://meta.discourse.org'
- )}`
+ 'https://meta.discourse.org',
+ )}`,
)
diff --git a/services/discourse/discourse.service.js b/services/discourse/discourse.service.js
index 2c017fa37d4c5..7d18db4f0dcd8 100644
--- a/services/discourse/discourse.service.js
+++ b/services/discourse/discourse.service.js
@@ -1,38 +1,42 @@
-'use strict'
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger, url } from '../validators.js'
+import { BaseJsonService, queryParams } from '../index.js'
-const camelcase = require('camelcase')
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger, optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
+const schemaSingular = Joi.object({
topic_count: nonNegativeInteger,
user_count: nonNegativeInteger,
post_count: nonNegativeInteger,
like_count: nonNegativeInteger,
}).required()
+const schemaPlural = Joi.object({
+ topics_count: nonNegativeInteger,
+ users_count: nonNegativeInteger,
+ posts_count: nonNegativeInteger,
+ likes_count: nonNegativeInteger,
+})
+
+const schema = Joi.alternatives(schemaSingular, schemaPlural)
+
const queryParamSchema = Joi.object({
- server: optionalUrl.required(),
+ server: url,
}).required()
-class DiscourseBase extends BaseJsonService {
- static get category() {
- return 'chat'
- }
+function singular(variant) {
+ return variant.slice(0, -1)
+}
- static buildRoute(metric) {
- return {
- base: 'discourse',
- pattern: metric,
- queryParamSchema,
- }
- }
+const params = queryParams({
+ name: 'server',
+ example: 'https://meta.discourse.org',
+ required: true,
+})
- static get defaultBadgeData() {
- return { label: 'discourse' }
- }
+class DiscourseBase extends BaseJsonService {
+ static category = 'chat'
+
+ static defaultBadgeData = { label: 'discourse' }
async fetch({ server }) {
return this._requestJson({
@@ -42,61 +46,60 @@ class DiscourseBase extends BaseJsonService {
}
}
-function DiscourseMetricIntegrationFactory({ metricName, property }) {
- return class DiscourseMetric extends DiscourseBase {
- static get name() {
- // The space is needed so we get 'DiscourseTopics' rather than
- // 'Discoursetopics'. `camelcase()` removes it.
- return camelcase(`Discourse ${metricName}`, { pascalCase: true })
- }
-
- static get route() {
- return this.buildRoute(metricName)
- }
+class DiscourseMetric extends DiscourseBase {
+ static route = {
+ base: 'discourse',
+ pattern: ':variant(topics|users|posts|likes)',
+ queryParamSchema,
+ }
- static get examples() {
- return [
- {
- title: `Discourse ${metricName}`,
- namedParams: {},
- queryParams: {
- server: 'https://meta.discourse.org',
- },
- staticPreview: this.render({ stat: 100 }),
- },
- ]
- }
+ static openApi = {
+ '/discourse/topics': {
+ get: { summary: 'Discourse Topics', parameters: params },
+ },
+ '/discourse/users': {
+ get: { summary: 'Discourse Users', parameters: params },
+ },
+ '/discourse/posts': {
+ get: { summary: 'Discourse Posts', parameters: params },
+ },
+ '/discourse/likes': {
+ get: { summary: 'Discourse Likes', parameters: params },
+ },
+ }
- static render({ stat }) {
- return {
- message: `${metric(stat)} ${metricName}`,
- color: 'brightgreen',
- }
+ static render({ variant, stat }) {
+ return {
+ message: `${metric(stat)} ${variant}`,
+ color: 'brightgreen',
}
+ }
- async handle(_routeParams, { server }) {
- const data = await this.fetch({ server })
- return this.constructor.render({ stat: data[property] })
+ async handle({ variant }, { server }) {
+ const data = await this.fetch({ server })
+ // e.g. variant == 'topics' --> try 'topic_count' then 'topics_count'
+ let stat = data[`${singular(variant)}_count`]
+ if (stat === undefined) {
+ stat = data[`${variant}_count`]
}
+ return this.constructor.render({ variant, stat })
}
}
class DiscourseStatus extends DiscourseBase {
- static get route() {
- return this.buildRoute('status')
+ static route = {
+ base: 'discourse',
+ pattern: 'status',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: `Discourse status`,
- namedParams: {},
- queryParams: {
- server: 'https://meta.discourse.org',
- },
- staticPreview: this.render(),
+ static openApi = {
+ '/discourse/status': {
+ get: {
+ summary: 'Discourse Status',
+ parameters: params,
},
- ]
+ },
}
static render() {
@@ -114,11 +117,4 @@ class DiscourseStatus extends DiscourseBase {
}
}
-const metricIntegrations = [
- { metricName: 'topics', property: 'topic_count' },
- { metricName: 'users', property: 'user_count' },
- { metricName: 'posts', property: 'post_count' },
- { metricName: 'likes', property: 'like_count' },
-].map(DiscourseMetricIntegrationFactory)
-
-module.exports = [...metricIntegrations, DiscourseStatus]
+export default [DiscourseMetric, DiscourseStatus]
diff --git a/services/discourse/discourse.tester.js b/services/discourse/discourse.tester.js
index b357ddb2861ed..763264a31255a 100644
--- a/services/discourse/discourse.tester.js
+++ b/services/discourse/discourse.tester.js
@@ -1,90 +1,110 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'discourse',
title: 'Discourse',
-}))
+})
-const data = {
- topic_count: 22513,
- post_count: 337719,
- user_count: 31220,
- topics_7_days: 143,
- topics_30_days: 551,
- posts_7_days: 2679,
- posts_30_days: 10445,
- users_7_days: 204,
- users_30_days: 803,
- active_users_7_days: 762,
- active_users_30_days: 1495,
- like_count: 308833,
- likes_7_days: 3633,
- likes_30_days: 13397,
-}
+const dataCases = [
+ {
+ // Singular form
+ topic_count: 22513,
+ post_count: 337719,
+ user_count: 31220,
+ topics_7_days: 143,
+ topics_30_days: 551,
+ posts_7_days: 2679,
+ posts_30_days: 10445,
+ users_7_days: 204,
+ users_30_days: 803,
+ active_users_7_days: 762,
+ active_users_30_days: 1495,
+ like_count: 308833,
+ likes_7_days: 3633,
+ likes_30_days: 13397,
+ },
+ {
+ // Plural form
+ topics_count: 22513,
+ posts_count: 337719,
+ users_count: 31220,
+ topics_7_days: 143,
+ topics_30_days: 551,
+ posts_7_days: 2679,
+ posts_30_days: 10445,
+ users_7_days: 204,
+ users_30_days: 803,
+ active_users_7_days: 762,
+ active_users_30_days: 1495,
+ likes_count: 308833,
+ likes_7_days: 3633,
+ likes_30_days: 13397,
+ },
+]
-t.create('Topics')
- .get('/topics.json?server=https://meta.discourse.org')
- .intercept(nock =>
- nock('https://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: '23k topics' })
+dataCases.forEach(data => {
+ t.create('Topics')
+ .get('/topics.json?server=https://meta.discourse.org')
+ .intercept(nock =>
+ nock('https://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: '23k topics' })
-t.create('Posts')
- .get('/posts.json?server=https://meta.discourse.org')
- .intercept(nock =>
- nock('https://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: '338k posts' })
+ t.create('Posts')
+ .get('/posts.json?server=https://meta.discourse.org')
+ .intercept(nock =>
+ nock('https://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: '338k posts' })
-t.create('Users')
- .get('/users.json?server=https://meta.discourse.org')
- .intercept(nock =>
- nock('https://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: '31k users' })
+ t.create('Users')
+ .get('/users.json?server=https://meta.discourse.org')
+ .intercept(nock =>
+ nock('https://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: '31k users' })
-t.create('Likes')
- .get('/likes.json?server=https://meta.discourse.org')
- .intercept(nock =>
- nock('https://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: '309k likes' })
+ t.create('Likes')
+ .get('/likes.json?server=https://meta.discourse.org')
+ .intercept(nock =>
+ nock('https://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: '309k likes' })
-t.create('Status')
- .get('/status.json?server=https://meta.discourse.org')
- .intercept(nock =>
- nock('https://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: 'online' })
+ t.create('Status')
+ .get('/status.json?server=https://meta.discourse.org')
+ .intercept(nock =>
+ nock('https://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: 'online' })
-t.create('Status with http (not https)')
- .get('/status.json?server=http://meta.discourse.org')
- .intercept(nock =>
- nock('http://meta.discourse.org')
- .get('/site/statistics.json')
- .reply(200, data)
- )
- .expectBadge({ label: 'discourse', message: 'online' })
+ t.create('Status with http (not https)')
+ .get('/status.json?server=http://meta.discourse.org')
+ .intercept(nock =>
+ nock('http://meta.discourse.org')
+ .get('/site/statistics.json')
+ .reply(200, data),
+ )
+ .expectBadge({ label: 'discourse', message: 'online' })
+})
t.create('Invalid Host')
.get('/status.json?server=https://some.host')
.intercept(nock =>
nock('https://some.host')
.get('/site/statistics.json')
- .reply(404, 'Not Found ')
+ .reply(404, 'Not Found '),
)
.expectBadge({ label: 'discourse', message: 'not found' })
@@ -93,7 +113,7 @@ t.create('Topics')
.expectBadge({
label: 'discourse',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) topics$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) topics$/,
),
})
@@ -102,7 +122,7 @@ t.create('Posts')
.expectBadge({
label: 'discourse',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) posts$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) posts$/,
),
})
@@ -111,7 +131,7 @@ t.create('Users')
.expectBadge({
label: 'discourse',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) users$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) users$/,
),
})
@@ -120,7 +140,7 @@ t.create('Likes')
.expectBadge({
label: 'discourse',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) likes$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) likes$/,
),
})
diff --git a/services/dockbit/dockbit.service.js b/services/dockbit/dockbit.service.js
deleted file mode 100644
index f8b2e9028487b..0000000000000
--- a/services/dockbit/dockbit.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'build',
- route: {
- base: 'dockbit',
- pattern: ':various+',
- },
- label: 'dockbit',
- dateAdded: new Date('2017-12-31'),
-})
diff --git a/services/dockbit/dockbit.tester.js b/services/dockbit/dockbit.tester.js
deleted file mode 100644
index 4d9ef389a8321..0000000000000
--- a/services/dockbit/dockbit.tester.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'dockbit',
- title: 'Dockbit',
-}))
-
-t.create('no longer available (previously image size)')
- .get('/image-size/_/ubuntu/latest.json')
- .expectBadge({
- label: 'dockbit',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously number of layers)')
- .get('/layers/_/ubuntu/latest.json')
- .expectBadge({
- label: 'dockbit',
- message: 'no longer available',
- })
diff --git a/services/docker/docker-automated.service.js b/services/docker/docker-automated.service.js
index 509a85a19a64d..1dc7b4b81eee7 100644
--- a/services/docker/docker-automated.service.js
+++ b/services/docker/docker-automated.service.js
@@ -1,42 +1,51 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-const {
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import {
dockerBlue,
buildDockerUrl,
getDockerHubUser,
-} = require('./docker-helpers')
+} from './docker-helpers.js'
+import { fetch } from './docker-hub-common-fetch.js'
const automatedBuildSchema = Joi.object({
is_automated: Joi.boolean().required(),
}).required()
-module.exports = class DockerAutomatedBuild extends BaseJsonService {
- static get category() {
- return 'build'
- }
+export default class DockerAutomatedBuild extends BaseJsonService {
+ static category = 'build'
+ static route = buildDockerUrl('automated')
- static get route() {
- return buildDockerUrl('automated')
+ static auth = {
+ userKey: 'dockerhub_username',
+ passKey: 'dockerhub_pat',
+ authorizedOrigins: [
+ 'https://hub.docker.com',
+ 'https://registry.hub.docker.com',
+ ],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Docker Automated build',
- namedParams: {
- user: 'jrottenberg',
- repo: 'ffmpeg',
- },
- staticPreview: this.render({ isAutomated: true }),
+ static openApi = {
+ '/docker/automated/{user}/{repo}': {
+ get: {
+ summary: 'Docker Automated build',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'jrottenberg',
+ },
+ {
+ name: 'repo',
+ example: 'ffmpeg',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'docker build' }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'docker build' }
static render({ isAutomated }) {
if (isAutomated) {
@@ -47,12 +56,12 @@ module.exports = class DockerAutomatedBuild extends BaseJsonService {
}
async fetch({ user, repo }) {
- return this._requestJson({
+ return await fetch(this, {
schema: automatedBuildSchema,
url: `https://registry.hub.docker.com/v2/repositories/${getDockerHubUser(
- user
+ user,
)}/${repo}`,
- errorMessages: { 404: 'repo not found' },
+ httpErrors: { 404: 'repo not found' },
})
}
diff --git a/services/docker/docker-automated.spec.js b/services/docker/docker-automated.spec.js
new file mode 100644
index 0000000000000..5d9ce4801b8ac
--- /dev/null
+++ b/services/docker/docker-automated.spec.js
@@ -0,0 +1,15 @@
+import { testAuth } from '../test-helpers.js'
+import DockerAutomatedBuild from './docker-automated.service.js'
+
+describe('DockerAutomatedBuild', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ DockerAutomatedBuild,
+ 'JwtAuth',
+ { is_automated: true },
+ { jwtLoginEndpoint: 'https://hub.docker.com/v2/users/login/' },
+ )
+ })
+ })
+})
diff --git a/services/docker/docker-automated.tester.js b/services/docker/docker-automated.tester.js
index f98d2144282d8..38e312de3a251 100644
--- a/services/docker/docker-automated.tester.js
+++ b/services/docker/docker-automated.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { dockerBlue } from './docker-helpers.js'
+export const t = await createServiceTester()
const isAutomatedBuildStatus = Joi.string().valid('automated', 'manual')
@@ -29,7 +28,7 @@ t.create('docker automated build - automated')
.intercept(nock =>
nock('https://registry.hub.docker.com/')
.get('/v2/repositories/library/ubuntu')
- .reply(200, { is_automated: true })
+ .reply(200, { is_automated: true }),
)
.expectBadge({
label: 'docker build',
@@ -42,6 +41,6 @@ t.create('docker automated build - manual')
.intercept(nock =>
nock('https://registry.hub.docker.com/')
.get('/v2/repositories/library/ubuntu')
- .reply(200, { is_automated: false })
+ .reply(200, { is_automated: false }),
)
.expectBadge({ label: 'docker build', message: 'manual', color: 'yellow' })
diff --git a/services/docker/docker-build.service.js b/services/docker/docker-build.service.js
deleted file mode 100644
index 341a7936c8c52..0000000000000
--- a/services/docker/docker-build.service.js
+++ /dev/null
@@ -1,67 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { anyInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-const {
- dockerBlue,
- buildDockerUrl,
- getDockerHubUser,
-} = require('./docker-helpers')
-
-const buildSchema = Joi.object({
- results: Joi.array()
- .items(Joi.object({ status: anyInteger }).required())
- .required(),
-}).required()
-
-module.exports = class DockerBuild extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return buildDockerUrl('build')
- }
-
- static get examples() {
- return [
- {
- title: 'Docker Build Status',
- namedParams: {
- user: 'jrottenberg',
- repo: 'ffmpeg',
- },
- staticPreview: this.render({ status: 10 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'docker build' }
- }
-
- static render({ status }) {
- if (status === 10) {
- return { message: 'passing', color: 'brightgreen' }
- } else if (status < 0) {
- return { message: 'failing', color: 'red' }
- }
- return { message: 'building', color: dockerBlue }
- }
-
- async fetch({ user, repo }) {
- return this._requestJson({
- schema: buildSchema,
- url: `https://registry.hub.docker.com/v2/repositories/${getDockerHubUser(
- user
- )}/${repo}/buildhistory`,
- errorMessages: { 404: 'repo not found' },
- })
- }
-
- async handle({ user, repo }) {
- const data = await this.fetch({ user, repo })
- return this.constructor.render({ status: data.results[0].status })
- }
-}
diff --git a/services/docker/docker-build.tester.js b/services/docker/docker-build.tester.js
deleted file mode 100644
index 533f5c52ac7fb..0000000000000
--- a/services/docker/docker-build.tester.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
-
-t.create('docker build status (valid, user)')
- .get('/jrottenberg/ffmpeg.json')
- .expectBadge({
- label: 'docker build',
- message: isBuildStatus,
- })
-
-t.create('docker build status (not found)')
- .get('/_/not-a-real-repo.json')
- .expectBadge({ label: 'docker build', message: 'repo not found' })
-
-t.create('docker build status (passing)')
- .get('/_/ubuntu.json')
- .intercept(nock =>
- nock('https://registry.hub.docker.com/')
- .get('/v2/repositories/library/ubuntu/buildhistory')
- .reply(200, { results: [{ status: 10 }] })
- )
- .expectBadge({
- label: 'docker build',
- message: 'passing',
- color: 'brightgreen',
- })
-
-t.create('docker build status (failing)')
- .get('/_/ubuntu.json')
- .intercept(nock =>
- nock('https://registry.hub.docker.com/')
- .get('/v2/repositories/library/ubuntu/buildhistory')
- .reply(200, { results: [{ status: -1 }] })
- )
- .expectBadge({ label: 'docker build', message: 'failing', color: 'red' })
-
-t.create('docker build status (building)')
- .get('/_/ubuntu.json')
- .intercept(nock =>
- nock('https://registry.hub.docker.com/')
- .get('/v2/repositories/library/ubuntu/buildhistory')
- .reply(200, { results: [{ status: 1 }] })
- )
- .expectBadge({
- label: 'docker build',
- message: 'building',
- color: `#${dockerBlue}`,
- })
diff --git a/services/docker/docker-cloud-automated.service.js b/services/docker/docker-cloud-automated.service.js
index 87cb99b365b61..f31a99708757d 100644
--- a/services/docker/docker-cloud-automated.service.js
+++ b/services/docker/docker-cloud-automated.service.js
@@ -1,48 +1,11 @@
-'use strict'
-
-const { BaseJsonService } = require('..')
-const { dockerBlue, buildDockerUrl } = require('./docker-helpers')
-const { fetchBuild } = require('./docker-cloud-common-fetch')
-
-module.exports = class DockerCloudAutomatedBuild extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return buildDockerUrl('cloud/automated')
- }
-
- static get examples() {
- return [
- {
- title: 'Docker Cloud Automated build',
- documentation:
- 'For the new Docker Hub (https://cloud.docker.com)
',
- namedParams: {
- user: 'jrottenberg',
- repo: 'ffmpeg',
- },
- staticPreview: this.render({ buildSettings: ['test'] }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'docker build' }
- }
-
- static render({ buildSettings }) {
- if (buildSettings.length >= 1) {
- return { message: 'automated', color: dockerBlue }
- }
- return { message: 'manual', color: 'yellow' }
- }
-
- async handle({ user, repo }) {
- const data = await fetchBuild(this, { user, repo })
- return this.constructor.render({
- buildSettings: data.objects[0].build_settings,
- })
- }
-}
+import { retiredService } from '../index.js'
+
+export default retiredService({
+ category: 'build',
+ route: {
+ base: 'docker/cloud/automated',
+ pattern: ':user/:repo',
+ },
+ label: 'dockercloud',
+ dateAdded: new Date('2025-11-15'),
+})
diff --git a/services/docker/docker-cloud-automated.tester.js b/services/docker/docker-cloud-automated.tester.js
index e3827f21b51c4..d616c93d6be85 100644
--- a/services/docker/docker-cloud-automated.tester.js
+++ b/services/docker/docker-cloud-automated.tester.js
@@ -1,49 +1,14 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
+export const t = new ServiceTester({
+ id: 'dockercloudautomated',
+ title: 'DockerCloudAutomated',
+ pathPrefix: '/docker/cloud/automated',
+})
-const isAutomatedBuildStatus = Joi.string().valid('automated', 'manual')
-
-t.create('docker cloud automated build (valid, user)')
- .get('/jrottenberg/ffmpeg.json')
- .expectBadge({
- label: 'docker build',
- message: isAutomatedBuildStatus,
- })
-
-t.create('docker cloud automated build (not found)')
- .get('/badges/not-a-real-repo.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(
- `/api/build/v1/source?image=${encodeURIComponent(
- 'badges/not-a-real-repo'
- )}`
- )
- .reply(404, { detail: 'Object not found' })
- )
- .expectBadge({ label: 'docker build', message: 'repo not found' })
-
-t.create('docker cloud automated build - automated')
- .get('/xenolf/lego.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(`/api/build/v1/source?image=${encodeURIComponent('xenolf/lego')}`)
- .reply(200, { objects: [{ build_settings: ['test1'] }] })
- )
+t.create('docker cloud automated build')
+ .get('/pavics/magpie.json')
.expectBadge({
- label: 'docker build',
- message: 'automated',
- color: `#${dockerBlue}`,
+ label: 'dockercloud',
+ message: 'retired badge',
})
-
-t.create('docker cloud automated build - manual')
- .get('/xenolf/lego.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(`/api/build/v1/source?image=${encodeURIComponent('xenolf/lego')}`)
- .reply(200, { objects: [{ build_settings: [] }] })
- )
- .expectBadge({ label: 'docker build', message: 'manual', color: 'yellow' })
diff --git a/services/docker/docker-cloud-build.service.js b/services/docker/docker-cloud-build.service.js
index db194e8cddfc8..74aa04152f117 100644
--- a/services/docker/docker-cloud-build.service.js
+++ b/services/docker/docker-cloud-build.service.js
@@ -1,49 +1,11 @@
-'use strict'
-
-const { BaseJsonService } = require('..')
-const { dockerBlue, buildDockerUrl } = require('./docker-helpers')
-const { fetchBuild } = require('./docker-cloud-common-fetch')
-
-module.exports = class DockerCloudBuild extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return buildDockerUrl('cloud/build')
- }
-
- static get examples() {
- return [
- {
- title: 'Docker Cloud Build Status',
- documentation:
- 'For the new Docker Hub (https://cloud.docker.com)
',
- namedParams: {
- user: 'jrottenberg',
- repo: 'ffmpeg',
- },
- staticPreview: this.render({ state: 'Success' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'docker build' }
- }
-
- static render({ state }) {
- if (state === 'Success') {
- return { message: 'passing', color: 'brightgreen' }
- }
- if (state === 'Failed') {
- return { message: 'failing', color: 'red' }
- }
- return { message: 'building', color: dockerBlue }
- }
-
- async handle({ user, repo }) {
- const data = await fetchBuild(this, { user, repo })
- return this.constructor.render({ state: data.objects[0].state })
- }
-}
+import { retiredService } from '../index.js'
+
+export default retiredService({
+ category: 'build',
+ route: {
+ base: 'docker/cloud/build',
+ pattern: ':user/:repo',
+ },
+ label: 'dockercloud',
+ dateAdded: new Date('2025-11-15'),
+})
diff --git a/services/docker/docker-cloud-build.tester.js b/services/docker/docker-cloud-build.tester.js
index 59a8f8ef7223d..b4be55e6e9658 100644
--- a/services/docker/docker-cloud-build.tester.js
+++ b/services/docker/docker-cloud-build.tester.js
@@ -1,60 +1,12 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
-
-t.create('docker cloud build status (valid, user)')
- .get('/jrottenberg/ffmpeg.json')
- .expectBadge({
- label: 'docker build',
- message: isBuildStatus,
- })
-
-t.create('docker cloud build status (not found)')
- .get('/badges/not-a-real-repo.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(
- `/api/build/v1/source?image=${encodeURIComponent(
- 'badges/not-a-real-repo'
- )}`
- )
- .reply(404, { detail: 'Object not found' })
- )
- .expectBadge({ label: 'docker build', message: 'repo not found' })
-
-t.create('docker cloud build status (passing)')
- .get('/xenolf/lego.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(`/api/build/v1/source?image=${encodeURIComponent('xenolf/lego')}`)
- .reply(200, { objects: [{ state: 'Success' }] })
- )
- .expectBadge({
- label: 'docker build',
- message: 'passing',
- color: 'brightgreen',
- })
-
-t.create('docker cloud build status (failing)')
- .get('/xenolf/lego.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(`/api/build/v1/source?image=${encodeURIComponent('xenolf/lego')}`)
- .reply(200, { objects: [{ state: 'Failed' }] })
- )
- .expectBadge({ label: 'docker build', message: 'failing', color: 'red' })
-
-t.create('docker cloud build status (building)')
- .get('/xenolf/lego.json')
- .intercept(nock =>
- nock('https://cloud.docker.com/')
- .get(`/api/build/v1/source?image=${encodeURIComponent('xenolf/lego')}`)
- .reply(200, { objects: [{ state: 'Empty' }] })
- )
- .expectBadge({
- label: 'docker build',
- message: 'building',
- color: `#${dockerBlue}`,
- })
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'dockercloudbuild',
+ title: 'DockerCloudBuild',
+ pathPrefix: '/docker/cloud/build',
+})
+
+t.create('docker cloud build status').get('/pavics/magpie.json').expectBadge({
+ label: 'dockercloud',
+ message: 'retired badge',
+})
diff --git a/services/docker/docker-cloud-common-fetch.js b/services/docker/docker-cloud-common-fetch.js
deleted file mode 100644
index 2f10c52eebf1a..0000000000000
--- a/services/docker/docker-cloud-common-fetch.js
+++ /dev/null
@@ -1,27 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-
-const cloudBuildSchema = Joi.object({
- objects: Joi.array()
- .items(
- Joi.object({
- state: Joi.string(),
- build_settings: Joi.array(),
- }).required()
- )
- .required(),
-}).required()
-
-async function fetchBuild(serviceInstance, { user, repo }) {
- return serviceInstance._requestJson({
- schema: cloudBuildSchema,
- url: `https://cloud.docker.com/api/build/v1/source`,
- options: { qs: { image: `${user}/${repo}` } },
- errorMessages: { 404: 'repo not found' },
- })
-}
-
-module.exports = {
- fetchBuild,
-}
diff --git a/services/docker/docker-fixtures.js b/services/docker/docker-fixtures.js
index 8adb09ecb9410..5f0001af3f5d6 100644
--- a/services/docker/docker-fixtures.js
+++ b/services/docker/docker-fixtures.js
@@ -1,73 +1,36 @@
-'use strict'
-
const sizeDataNoTagSemVerSort = [
- { name: 'master', full_size: 13449470 },
- { name: 'feature-smtps-support', full_size: 13449638 },
- { name: 'latest', full_size: 13448411 },
- { name: '4', full_size: 13448411 },
- { name: '4.3', full_size: 13448411 },
- { name: '4.3.0', full_size: 13448411 },
- { name: '4.2', full_size: 13443674 },
- { name: '4.2.0', full_size: 13443674 },
- { name: '4.1', full_size: 19244435 },
- { name: '4.1.0', full_size: 19244435 },
- { name: 'v4.0.0-alpha2', full_size: 10933605 },
- { name: 'v4.0.0-alpha1', full_size: 10933644 },
- { name: '4.0.0', full_size: 11512227 },
- { name: '4.0', full_size: 11512227 },
- { name: 'v2.1.9', full_size: 29739490 },
- { name: 'v2.1.10', full_size: 29739842 },
- { name: 'v3.0.0', full_size: 32882980 },
- { name: 'v3.0.1', full_size: 32880923 },
- { name: 'v3.1.0', full_size: 32441549 },
- { name: 'v3.1.1', full_size: 32441767 },
- { name: 'v3.1.2', full_size: 32442741 },
- { name: 'v3.1.3', full_size: 32442629 },
- { name: 'v3.1.4', full_size: 32478607 },
- { name: 'v3.2.0', full_size: 33489914 },
- { name: 'v3.3.0', full_size: 33628545 },
- { name: 'v3.3.1', full_size: 33629018 },
- { name: 'v3.3.3', full_size: 33628988 },
- { name: 'v3.3.4', full_size: 33629019 },
- { name: 'v3.3.6', full_size: 33628753 },
- { name: 'v3.3.7', full_size: 33629556 },
- { name: 'v3.3.8', full_size: 33644261 },
- { name: 'v3.3.9', full_size: 33644175 },
- { name: 'v3.3.10', full_size: 33644406 },
- { name: 'v3.3.11', full_size: 33644430 },
- { name: 'v3.3.12', full_size: 33644703 },
- { name: 'v3.3.13', full_size: 33644377 },
- { name: 'v3.3.15', full_size: 33644581 },
- { name: 'v3.3.16', full_size: 33644663 },
- { name: 'v3.3.17', full_size: 33644228 },
- { name: 'v3.3.18', full_size: 33644466 },
- { name: 'v3.3.19', full_size: 33644724 },
- { name: 'v3.4.0', full_size: 34918552 },
- { name: 'v3.4.2', full_size: 33605129 },
- { name: 'v3.5.0', full_size: 33582915 },
- { name: 'v3.6.0', full_size: 34789944 },
- { name: 'develop', full_size: 38129308 },
- { name: 'v3.7.0', full_size: 38179583 },
- { name: 'v3.7.1', full_size: 38614944 },
- { name: 'v3.8.0', full_size: 42962384 },
- { name: 'v3.8.1', full_size: 40000713 },
- { name: 'v3.8.2', full_size: 40000567 },
- { name: 'v3.8.3', full_size: 40040963 },
- { name: 'v3.9.0', full_size: 40044357 },
- { name: 'v3.9.1', full_size: 40048123 },
- { name: 'v3.9.2', full_size: 40047663 },
- { name: 'v3.9.3', full_size: 40048204 },
- { name: 'v3.9.4', full_size: 40049571 },
- { name: 'v3.9.5', full_size: 40049695 },
- { name: 'v3.10.0', full_size: 39940736 },
- { name: 'v3.11.0', full_size: 39928170 },
- { name: 'v3.12.0', full_size: 39966770 },
- { name: 'v3.13.0', full_size: 38556045 },
- { name: 'v3.14.0', full_size: 38574008 },
- { name: 'v3.15.0', full_size: 38578507 },
- { name: 'v3.16.0', full_size: 38852598 },
- { name: 'v3.16.1', full_size: 38851702 },
- { name: 'v3.16.2', full_size: 38969822 },
+ {
+ full_size: 300000000,
+ name: 'v4.0.0-alpha2',
+ images: [
+ { architecture: 'amd64', size: 219939484 },
+ { architecture: 'arm64', size: 200000000 },
+ ],
+ },
+ {
+ full_size: 400000000,
+ name: 'v4.2.4',
+ images: [
+ { architecture: 'amd64', size: 220000000 },
+ { architecture: 'arm64', size: 210000000 },
+ ],
+ },
+ {
+ full_size: 100000000,
+ name: 'v3.9.7',
+ images: [
+ { architecture: 'amd64', size: 120000000 },
+ { architecture: 'arm64', size: 110000000 },
+ ],
+ },
+ {
+ full_size: 500000000,
+ name: 'latest',
+ images: [
+ { architecture: 'amd64', size: 560000000 },
+ { architecture: 'arm64', size: 460000000 },
+ ],
+ },
]
const versionDataNoTagDateSort = {
count: 4,
@@ -2935,10 +2898,155 @@ const versionDataWithTag = [
{ name: '2.6', images: [] },
]
-module.exports = {
+const versionDataWithVaryingArchitectures = [
+ {
+ name: '3.9.5',
+ images: [
+ {
+ digest:
+ 'sha256:fa5361fbf636d3ac50cb529dab20d810eb466de2079f5710cef4cbada29cf499',
+ architecture: 'ppc64le',
+ },
+ {
+ digest:
+ 'sha256:97e9e9a15ef94526018e2fabfdfff14781e58b87f989d2e70543f296dcad26c6',
+ architecture: 's390x',
+ },
+ {
+ digest:
+ 'sha256:cae6522b6a351615e547ae9222c9a05d172bc5c3240eec03072d4e1d0429a17a',
+ architecture: 'arm64',
+ },
+ ],
+ },
+ {
+ name: '3.9',
+ images: [
+ {
+ digest:
+ 'sha256:c7b3e8392e08c971e98627e2bddd10c7fa9d2eae797a16bc94de9709bb9300d0',
+ architecture: '386',
+ },
+ {
+ digest:
+ 'sha256:5292cebaf695db860087c5582d340a406613891b2819092747b0388da47936c8',
+ architecture: 'arm',
+ },
+ ],
+ },
+ {
+ name: '3.8.5',
+ images: [
+ {
+ digest:
+ 'sha256:dabea2944dcc2b86482b4f0b0fb62da80e0673e900c46c0e03b45919881a5d84',
+ architecture: 'arm',
+ },
+ {
+ digest:
+ 'sha256:514ec80ffbe1a2ab1d9a3d5e6082296296a1d8b6870246edf897228e5df2367d',
+ architecture: 's390x',
+ },
+ {
+ digest:
+ 'sha256:ab3fe83c0696e3f565c9b4a734ec309ae9bd0d74c192de4590fd6dc2ef717815',
+ architecture: 'amd64',
+ },
+ ],
+ },
+ {
+ name: '3.8',
+ images: [
+ {
+ digest:
+ 'sha256:cf35b4fa14e23492df67af08ced54a15e68ad00cac545b437b1994340f20648c',
+ architecture: '386',
+ },
+ {
+ digest:
+ 'sha256:514ec80ffbe1a2ab1d9a3d5e6082296296a1d8b6870246edf897228e5df2367d',
+ architecture: 's390x',
+ },
+ {
+ digest:
+ 'sha256:402d21757a03a114d273bbe372fa4b9eca567e8b6c332fa7ebf982b902207242',
+ architecture: 'ppc64le',
+ },
+ ],
+ },
+ {
+ name: '3.10.4',
+ images: [
+ {
+ digest:
+ 'sha256:2632d6288d34d7175021683f6e363fa7c0fa8866a565eb285e36e3b856545e82',
+ architecture: 'arm',
+ },
+ {
+ digest:
+ 'sha256:9afbfccb806687f6979661622f0c04dc534769e742465b107f84a830cbb8e77a',
+ architecture: 'arm',
+ },
+ {
+ digest:
+ 'sha256:33158d51a7a549207e508a42bf46493f23e1e99fbc011eb3f3742e8b349a2be9',
+ architecture: 'ppc64le',
+ },
+ ],
+ },
+ { name: '2.7', images: [] },
+ { name: '2.6', images: [] },
+]
+
+const versionDataWithArchSpecificVersions = [
+ {
+ name: '3.8-arm64',
+ images: [
+ {
+ digest:
+ 'sha256:3d62c3ceeba113f72efaaf3a35d83081963fa2cd29be76ed1aaa87b59f6f8eae',
+ architecture: 'arm64',
+ },
+ ],
+ },
+ {
+ name: '3.8-amd64',
+ images: [
+ {
+ digest:
+ 'sha256:661bdba24d94ee8f1e4002eb3a87d7062244dd2583fb294471e9e00a7bfba45d',
+ architecture: 'amd64',
+ },
+ ],
+ },
+ {
+ name: '3.9-arm64',
+ images: [
+ {
+ digest:
+ 'sha256:1c1d100e50a7684afcbdc82929de1bc79bee8adcaafd367c38e5aa7eed4d6e19',
+ architecture: 'arm64',
+ },
+ ],
+ },
+ {
+ name: '3.9-amd64',
+ images: [
+ {
+ digest:
+ 'sha256:faf4f034c82d40b66a7285a5e9d305f543a2726961cc6da11206770ef013244e',
+ architecture: 'amd64',
+ },
+ ],
+ },
+]
+
+export {
sizeDataNoTagSemVerSort,
versionDataNoTagDateSort,
versionPagedDataNoTagDateSort,
versionDataNoTagSemVerSort,
versionDataWithTag,
+ versionDataWithVaryingArchitectures,
+ versionDataWithArchSpecificVersions,
}
diff --git a/services/docker/docker-helpers.js b/services/docker/docker-helpers.js
index 87908b00f436a..b5816810c8f49 100644
--- a/services/docker/docker-helpers.js
+++ b/services/docker/docker-helpers.js
@@ -1,7 +1,30 @@
-'use strict'
+import Joi from 'joi'
+// see https://github.com/badges/shields/pull/1690
+import { NotFound } from '../index.js'
+const dockerBlue = '066da5'
-const dockerBlue = '066da5' // see https://github.com/badges/shields/pull/1690
-const { NotFound } = require('..')
+const archEnum = [
+ 'amd64',
+ 'arm',
+ 'arm64',
+ 's390x',
+ '386',
+ 'ppc64',
+ 'ppc64le',
+ 'wasm',
+ 'mips',
+ 'mipsle',
+ 'mips64',
+ 'mips64le',
+ 'riscv64',
+ 'loong64',
+]
+
+// Valid architecture values: https://golang.org/doc/install/source#environment (GOARCH)
+const archSchema = Joi.alternatives(
+ Joi.string().valid(...archEnum),
+ Joi.number().valid(386).cast('string'),
+)
function buildDockerUrl(badgeName, includeTagRoute) {
if (includeTagRoute) {
@@ -36,8 +59,8 @@ async function getMultiPageData({ user, repo, fetch }) {
const pageData = await Promise.all(
[...Array(numberOfPages - 1).keys()].map((_, i) =>
- fetch({ user, repo, page: ++i + 1 })
- )
+ fetch({ user, repo, page: ++i + 1 }),
+ ),
)
return [...data.results].concat(...pageData.map(p => p.results))
}
@@ -55,7 +78,9 @@ function getDigestSemVerMatches({ data, digest }) {
return version
}
-module.exports = {
+export {
+ archEnum,
+ archSchema,
dockerBlue,
buildDockerUrl,
getDockerHubUser,
diff --git a/services/docker/docker-hub-common-fetch.js b/services/docker/docker-hub-common-fetch.js
new file mode 100644
index 0000000000000..c5ef9c39e59e7
--- /dev/null
+++ b/services/docker/docker-hub-common-fetch.js
@@ -0,0 +1,10 @@
+async function fetch(serviceInstance, params) {
+ return serviceInstance._requestJson(
+ await serviceInstance.authHelper.withJwtAuth(
+ params,
+ 'https://hub.docker.com/v2/users/login/',
+ ),
+ )
+}
+
+export { fetch }
diff --git a/services/docker/docker-hub-common-fetch.spec.js b/services/docker/docker-hub-common-fetch.spec.js
new file mode 100644
index 0000000000000..0aac7416ca0ec
--- /dev/null
+++ b/services/docker/docker-hub-common-fetch.spec.js
@@ -0,0 +1,20 @@
+import sinon from 'sinon'
+import { expect } from 'chai'
+import { fetch } from './docker-hub-common-fetch.js'
+
+describe('fetch', function () {
+ it('invokes withJwtAuth', async function () {
+ const serviceInstance = {
+ _requestJson: sinon.stub().resolves('fake-response'),
+ authHelper: {
+ withJwtAuth: sinon.stub(),
+ },
+ }
+
+ const resp = await fetch(serviceInstance, {})
+
+ expect(serviceInstance.authHelper.withJwtAuth.calledOnce).to.be.true
+ expect(serviceInstance._requestJson.calledOnce).to.be.true
+ expect(resp).to.equal('fake-response')
+ })
+})
diff --git a/services/docker/docker-pulls.service.js b/services/docker/docker-pulls.service.js
index 1e9c0e7110993..eb1e27dc91ddb 100644
--- a/services/docker/docker-pulls.service.js
+++ b/services/docker/docker-pulls.service.js
@@ -1,59 +1,62 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-const {
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import {
dockerBlue,
buildDockerUrl,
getDockerHubUser,
-} = require('./docker-helpers')
+} from './docker-helpers.js'
+import { fetch } from './docker-hub-common-fetch.js'
const pullsSchema = Joi.object({
pull_count: nonNegativeInteger,
}).required()
-module.exports = class DockerPulls extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
+export default class DockerPulls extends BaseJsonService {
+ static category = 'downloads'
+ static route = buildDockerUrl('pulls')
- static get route() {
- return buildDockerUrl('pulls')
+ static auth = {
+ userKey: 'dockerhub_username',
+ passKey: 'dockerhub_pat',
+ authorizedOrigins: ['https://hub.docker.com'],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Docker Pulls',
- namedParams: {
- user: '_',
- repo: 'ubuntu',
- },
- staticPreview: this.render({ count: 765400000 }),
+ static openApi = {
+ '/docker/pulls/{user}/{repo}': {
+ get: {
+ summary: 'Docker Pulls',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: '_',
+ },
+ {
+ name: 'repo',
+ example: 'ubuntu',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'docker pulls' }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'docker pulls' }
- static render({ count }) {
- return {
- message: metric(count),
- color: dockerBlue,
- }
+ static render({ count: downloads }) {
+ return renderDownloadsBadge({ downloads, colorOverride: dockerBlue })
}
async fetch({ user, repo }) {
- return this._requestJson({
+ return await fetch(this, {
schema: pullsSchema,
url: `https://hub.docker.com/v2/repositories/${getDockerHubUser(
- user
+ user,
)}/${repo}`,
- errorMessages: { 404: 'repo not found' },
+ httpErrors: { 404: 'repo not found' },
})
}
diff --git a/services/docker/docker-pulls.tester.js b/services/docker/docker-pulls.tester.js
index cdf55b257441a..2482edea4acd7 100644
--- a/services/docker/docker-pulls.tester.js
+++ b/services/docker/docker-pulls.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import { dockerBlue } from './docker-helpers.js'
+export const t = await createServiceTester()
t.create('docker pulls (valid, library)')
.get('/_/ubuntu.json')
diff --git a/services/docker/docker-size.service.js b/services/docker/docker-size.service.js
index 0439181f3aec5..24eeaa35e86a9 100644
--- a/services/docker/docker-size.service.js
+++ b/services/docker/docker-size.service.js
@@ -1,19 +1,26 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { nonNegativeInteger } = require('../validators')
-const { latest } = require('../version')
-const { BaseJsonService, NotFound } = require('..')
-const {
+import Joi from 'joi'
+import { renderSizeBadge } from '../size.js'
+import { nonNegativeInteger } from '../validators.js'
+import { latest } from '../version.js'
+import { BaseJsonService, NotFound, pathParams, queryParams } from '../index.js'
+import {
+ archEnum,
+ archSchema,
buildDockerUrl,
getDockerHubUser,
getMultiPageData,
-} = require('./docker-helpers')
+} from './docker-helpers.js'
+import { fetch } from './docker-hub-common-fetch.js'
const buildSchema = Joi.object({
name: Joi.string().required(),
full_size: nonNegativeInteger.required(),
+ images: Joi.array().items(
+ Joi.object({
+ size: nonNegativeInteger.required(),
+ architecture: Joi.string().required(),
+ }),
+ ),
}).required()
const pagedSchema = Joi.object({
@@ -22,98 +29,191 @@ const pagedSchema = Joi.object({
Joi.object({
name: Joi.string().required(),
full_size: nonNegativeInteger.required(),
- })
+ images: Joi.array().items(
+ Joi.object({
+ size: nonNegativeInteger.required(),
+ architecture: Joi.string().required(),
+ }),
+ ),
+ }),
),
}).required()
+const sortEnum = ['date', 'semver']
+
const queryParamSchema = Joi.object({
sort: Joi.string()
- .valid('date', 'semver')
+ .valid(...sortEnum)
.default('date'),
+ arch: archSchema,
}).required()
-module.exports = class DockerSize extends BaseJsonService {
- static get category() {
- return 'size'
+const openApiQueryParams = queryParams(
+ {
+ name: 'sort',
+ example: 'semver',
+ schema: { type: 'string', enum: sortEnum },
+ description: 'If not specified, the default is `date`',
+ },
+ {
+ name: 'arch',
+ example: 'amd64',
+ schema: { type: 'string', enum: archEnum },
+ },
+)
+
+// If user provided the arch parameter,
+// check if any of the returned images has an architecture matching the arch parameter provided.
+// If yes, return the size of the image with this arch.
+// If not, throw the `NotFound` error.
+// For details see: https://github.com/badges/shields/issues/8238
+function getImageSizeForArch(images, arch) {
+ const imgWithArch = Object.values(images).find(
+ img => img.architecture === arch,
+ )
+
+ if (!imgWithArch) {
+ throw new NotFound({ prettyMessage: 'architecture not found' })
}
+ return imgWithArch.size
+}
+
+export default class DockerSize extends BaseJsonService {
+ static category = 'size'
+ static route = { ...buildDockerUrl('image-size', true), queryParamSchema }
- static get route() {
- return { ...buildDockerUrl('image-size', true), queryParamSchema }
+ static auth = {
+ userKey: 'dockerhub_username',
+ passKey: 'dockerhub_pat',
+ authorizedOrigins: [
+ 'https://hub.docker.com',
+ 'https://registry.hub.docker.com',
+ ],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Docker Image Size (latest by date)',
- pattern: ':user/:repo',
- namedParams: { user: 'fedora', repo: 'apache' },
- queryParams: { sort: 'date' },
- staticPreview: this.render({ size: 126000000 }),
+ static openApi = {
+ '/docker/image-size/{user}/{repo}': {
+ get: {
+ summary: 'Docker Image Size',
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: 'fedora' },
+ { name: 'repo', example: 'apache' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Docker Image Size (latest semver)',
- pattern: ':user/:repo',
- namedParams: { user: 'fedora', repo: 'apache' },
- queryParams: { sort: 'semver' },
- staticPreview: this.render({ size: 136000000 }),
+ },
+ '/docker/image-size/{user}/{repo}/{tag}': {
+ get: {
+ summary: 'Docker Image Size (tag)',
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: 'fedora' },
+ { name: 'repo', example: 'apache' },
+ { name: 'tag', example: 'latest' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Docker Image Size (tag)',
- pattern: ':user/:repo/:tag',
- namedParams: { user: 'fedora', repo: 'apache', tag: 'latest' },
- staticPreview: this.render({ size: 103000000 }),
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'image size',
- color: 'blue',
- }
- }
+ static _cacheLength = 900
- static render({ size }) {
- return { message: prettyBytes(size) }
- }
+ static defaultBadgeData = { label: 'image size', color: 'blue' }
async fetch({ user, repo, tag, page }) {
page = page ? `&page=${page}` : ''
- return this._requestJson({
+ return await fetch(this, {
schema: tag ? buildSchema : pagedSchema,
url: `https://registry.hub.docker.com/v2/repositories/${getDockerHubUser(
- user
+ user,
)}/${repo}/tags${
tag ? `/${tag}` : '?page_size=100&ordering=last_updated'
}${page}`,
- errorMessages: { 404: 'repository or tag not found' },
+ httpErrors: { 404: 'repository or tag not found' },
})
}
- transform({ tag, sort, data }) {
- if (!tag && sort === 'date') {
- if (data.count === 0) {
- throw new NotFound({ prettyMessage: 'repository not found' })
+ getSizeFromImageByLatestDate(data, arch) {
+ if (data.count === 0) {
+ throw new NotFound({ prettyMessage: 'repository not found' })
+ } else {
+ const latestEntry = data.results[0]
+
+ if (arch) {
+ return { size: getImageSizeForArch(latestEntry.images, arch) }
} else {
- return { size: data.results[0].full_size }
+ return { size: latestEntry.full_size }
}
- } else if (!tag && sort === 'semver') {
- const [matches, versions] = data.reduce(
- ([m, v], d) => {
- m[d.name] = d.full_size
- v.push(d.name)
- return [m, v]
- },
- [{}, []]
- )
- const version = latest(versions)
+ }
+ }
+
+ getSizeFromImageByLatestSemver(data, arch) {
+ // If no tag is specified, and sorting is by semver, first filter out the entry containing the latest semver from the response with Docker images.
+ // If no architecture is supplied by the user, return `full_size` from this entry.
+ // If the architecture is supplied by the user, check if any of the returned images for this entry has an architecture matching the arch parameter supplied by the user.
+ // If yes, return the size of the image with this arch.
+ // If not, throw the `NotFound` error.
+
+ const [matches, versions, images] = data.reduce(
+ ([m, v, i], d) => {
+ m[d.name] = d.full_size
+ v.push(d.name)
+ i[d.name] = d.images
+ return [m, v, i]
+ },
+ [{}, [], {}],
+ )
+
+ const version = latest(versions)
+
+ let sizeOfImgWithArch
+
+ if (arch) {
+ Object.keys(images).forEach(ver => {
+ if (ver === version) {
+ sizeOfImgWithArch = getImageSizeForArch(images[ver], arch)
+ return { size: sizeOfImgWithArch }
+ }
+ })
+
+ if (sizeOfImgWithArch) {
+ return { size: sizeOfImgWithArch }
+ } else {
+ throw new NotFound({ prettyMessage: 'architecture not found' })
+ }
+ } else {
return { size: matches[version] }
+ }
+ }
+
+ getSizeFromTag(data, arch) {
+ // If the tag is specified, and the architecture is supplied by the user,
+ // check if any of the returned images has an architecture matching the arch parameter supplied by the user.
+ // If yes, return the size of the image with this arch.
+ // If no, throw the `NotFound` error.
+ // If no architecture is supplied by the user, return the value of the `full_size` from the response (the image with the `latest` tag).
+ if (arch) {
+ return { size: getImageSizeForArch(data.images, arch) }
} else {
return { size: data.full_size }
}
}
- async handle({ user, repo, tag }, { sort }) {
+ transform({ tag, sort, data, arch }) {
+ if (!tag && sort === 'date') {
+ return this.getSizeFromImageByLatestDate(data, arch)
+ } else if (!tag && sort === 'semver') {
+ return this.getSizeFromImageByLatestSemver(data, arch)
+ } else {
+ return this.getSizeFromTag(data, arch)
+ }
+ }
+
+ async handle({ user, repo, tag }, { sort, arch }) {
let data
if (!tag && sort === 'date') {
@@ -128,7 +228,7 @@ module.exports = class DockerSize extends BaseJsonService {
data = await this.fetch({ user, repo, tag })
}
- const { size } = await this.transform({ tag, sort, data })
- return this.constructor.render({ size })
+ const { size } = await this.transform({ tag, sort, data, arch })
+ return renderSizeBadge(size, 'iec', 'image size')
}
}
diff --git a/services/docker/docker-size.spec.js b/services/docker/docker-size.spec.js
index 10140ebf022a6..b53cd4d67ab65 100644
--- a/services/docker/docker-size.spec.js
+++ b/services/docker/docker-size.spec.js
@@ -1,44 +1,101 @@
-'use strict'
+import { test, given } from 'sazerac'
+import DockerSize from './docker-size.service.js'
+import { sizeDataNoTagSemVerSort } from './docker-fixtures.js'
-const { test, given } = require('sazerac')
-const DockerSize = require('./docker-size.service')
-const { sizeDataNoTagSemVerSort } = require('./docker-fixtures')
-
-describe('DockerSize', function() {
- test(DockerSize.prototype.transform, () => {
- given({
- tag: '',
- sort: 'date',
- data: { results: [{ name: 'next', full_size: 219939484 }] },
- }).expect({
+describe('DockerSize', function () {
+ test(DockerSize.prototype.getSizeFromImageByLatestDate, () => {
+ given(
+ {
+ count: 0,
+ results: [],
+ },
+ 'amd64',
+ ).expectError('Not Found: repository not found')
+ given(
+ {
+ count: 1,
+ results: [
+ {
+ full_size: 300000000,
+ name: 'next',
+ images: [{ architecture: 'amd64', size: 219939484 }],
+ },
+ ],
+ },
+ 'amd64',
+ ).expect({
size: 219939484,
})
given({
- tag: '',
- sort: 'date',
- data: {
+ count: 1,
+ results: [
+ {
+ full_size: 300000000,
+ name: 'next',
+ images: [
+ { architecture: 'amd64', size: 219939484 },
+ { architecture: 'arm64', size: 200000000 },
+ ],
+ },
+ ],
+ }).expect({
+ size: 300000000,
+ })
+ given(
+ {
+ count: 1,
results: [
- { name: 'latest', full_size: 74661264 },
- { name: 'arm64v8-latest', full_size: 76310416 },
- { name: 'arm32v7-latest', full_size: 68001970 },
- { name: 'amd64-latest', full_size: 74661264 },
+ {
+ full_size: 300000000,
+ name: 'next',
+ images: [
+ { architecture: 'amd64', size: 219939484 },
+ { architecture: 'arm64', size: 200000000 },
+ ],
+ },
],
},
- }).expect({
- size: 74661264,
+ 'arm64777',
+ ).expectError('Not Found: architecture not found')
+ })
+
+ test(DockerSize.prototype.getSizeFromTag, () => {
+ given(
+ {
+ full_size: 300000000,
+ name: 'next',
+ images: [{ architecture: 'amd64', size: 219939484 }],
+ },
+ 'amd64',
+ ).expect({
+ size: 219939484,
})
given({
- tag: '',
- sort: 'semver',
- data: sizeDataNoTagSemVerSort,
+ full_size: 300000000,
+ name: 'next',
+ images: [{ architecture: 'amd64', size: 219939484 }],
}).expect({
- size: 13448411,
+ size: 300000000,
})
- given({
- tag: 'latest',
- data: { name: 'latest', full_size: 13448411 },
- }).expect({
- size: 13448411,
+ given(
+ {
+ full_size: 300000000,
+ name: 'next',
+ images: [{ architecture: 'amd64', size: 219939484 }],
+ },
+ 'arm64777',
+ ).expectError('Not Found: architecture not found')
+ })
+
+ test(DockerSize.prototype.getSizeFromImageByLatestSemver, () => {
+ given(sizeDataNoTagSemVerSort, 'amd64').expect({
+ size: 220000000,
+ })
+ given(sizeDataNoTagSemVerSort).expect({
+ size: 400000000,
})
+ given(sizeDataNoTagSemVerSort, 'nonexistentArch').expectError(
+ 'Not Found: architecture not found',
+ )
})
})
diff --git a/services/docker/docker-size.tester.js b/services/docker/docker-size.tester.js
index 6707e88967ab8..a5f078946e13c 100644
--- a/services/docker/docker-size.tester.js
+++ b/services/docker/docker-size.tester.js
@@ -1,34 +1,40 @@
-'use strict'
-
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIecFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('docker image size (valid, library)')
.get('/_/alpine.json')
.expectBadge({
label: 'image size',
- message: isFileSize,
+ message: isIecFileSize,
+ })
+
+t.create('docker image size (valid, library, arch parameter )')
+ .get('/_/mysql.json?arch=amd64')
+ .expectBadge({
+ label: 'image size',
+ message: isIecFileSize,
})
t.create('docker image size (valid, library with tag)')
.get('/_/alpine/latest.json')
.expectBadge({
label: 'image size',
- message: isFileSize,
+ message: isIecFileSize,
})
t.create('docker image size (valid, user)')
.get('/jrottenberg/ffmpeg.json')
.expectBadge({
label: 'image size',
- message: isFileSize,
+ message: isIecFileSize,
})
t.create('docker image size (valid, user with tag)')
.get('/jrottenberg/ffmpeg/3.2-alpine.json')
.expectBadge({
label: 'image size',
- message: isFileSize,
+ message: isIecFileSize,
})
t.create('docker image size (invalid, incorrect tag)')
@@ -42,5 +48,19 @@ t.create('docker image size (invalid, unknown repository)')
.get('/_/not-a-real-repo.json')
.expectBadge({
label: 'image size',
- message: 'repository not found',
+ message: 'repository or tag not found',
+ })
+
+t.create('docker image size (invalid, wrong sorting method)')
+ .get('/jrottenberg/ffmpeg/3.2-alpine.json?sort=daterrr')
+ .expectBadge({
+ label: 'image size',
+ message: 'invalid query parameter: sort',
+ })
+
+t.create('docker image size (invalid, nonexisting arch)')
+ .get('/jrottenberg/ffmpeg/3.2-alpine.json?arch=nonexistingArch')
+ .expectBadge({
+ label: 'image size',
+ message: 'invalid query parameter: arch',
})
diff --git a/services/docker/docker-stars.service.js b/services/docker/docker-stars.service.js
index 417bc80b30af5..588d9849bfb0a 100644
--- a/services/docker/docker-stars.service.js
+++ b/services/docker/docker-stars.service.js
@@ -1,39 +1,50 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseService } = require('..')
-const {
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import {
dockerBlue,
buildDockerUrl,
getDockerHubUser,
-} = require('./docker-helpers')
+} from './docker-helpers.js'
+import { fetch } from './docker-hub-common-fetch.js'
-module.exports = class DockerStars extends BaseService {
- static get category() {
- return 'rating'
- }
+const schema = Joi.object({
+ star_count: nonNegativeInteger.required(),
+}).required()
+
+export default class DockerStars extends BaseJsonService {
+ static category = 'rating'
+ static route = buildDockerUrl('stars')
- static get route() {
- return buildDockerUrl('stars')
+ static auth = {
+ userKey: 'dockerhub_username',
+ passKey: 'dockerhub_pat',
+ authorizedOrigins: ['https://hub.docker.com'],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Docker Stars',
- namedParams: {
- user: '_',
- repo: 'ubuntu',
- },
- staticPreview: this.render({ stars: 9000 }),
+ static openApi = {
+ '/docker/stars/{user}/{repo}': {
+ get: {
+ summary: 'Docker Stars',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: '_',
+ },
+ {
+ name: 'repo',
+ example: 'ubuntu',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'docker stars' }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'docker stars' }
static render({ stars }) {
return {
@@ -43,18 +54,17 @@ module.exports = class DockerStars extends BaseService {
}
async fetch({ user, repo }) {
- const url = `https://hub.docker.com/v2/repositories/${getDockerHubUser(
- user
- )}/${repo}/stars/count/`
- const { buffer } = await this._request({
- url,
- errorMessages: { 404: 'repo not found' },
+ return await fetch(this, {
+ schema,
+ url: `https://hub.docker.com/v2/repositories/${getDockerHubUser(
+ user,
+ )}/${repo}/`,
+ httpErrors: { 404: 'repo not found' },
})
- return this.constructor._validate(buffer, nonNegativeInteger)
}
async handle({ user, repo }) {
- const stars = await this.fetch({ user, repo })
- return this.constructor.render({ stars })
+ const resp = await this.fetch({ user, repo })
+ return this.constructor.render({ stars: resp.star_count })
}
}
diff --git a/services/docker/docker-stars.tester.js b/services/docker/docker-stars.tester.js
index 9a732e8564316..ebd715c3372e3 100644
--- a/services/docker/docker-stars.tester.js
+++ b/services/docker/docker-stars.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-const { dockerBlue } = require('./docker-helpers')
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import { dockerBlue } from './docker-helpers.js'
+export const t = await createServiceTester()
t.create('docker stars (valid, library)')
.get('/_/ubuntu.json')
diff --git a/services/docker/docker-version.service.js b/services/docker/docker-version.service.js
index 3640d89c98c23..b211fbfec609b 100644
--- a/services/docker/docker-version.service.js
+++ b/services/docker/docker-version.service.js
@@ -1,15 +1,22 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { latest, renderVersionBadge } = require('../version')
-const { BaseJsonService, NotFound, InvalidResponse } = require('..')
-const {
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { latest, renderVersionBadge } from '../version.js'
+import {
+ BaseJsonService,
+ NotFound,
+ InvalidResponse,
+ pathParams,
+ queryParams,
+} from '../index.js'
+import {
+ archEnum,
+ archSchema,
buildDockerUrl,
getDockerHubUser,
getMultiPageData,
getDigestSemVerMatches,
-} = require('./docker-helpers')
+} from './docker-helpers.js'
+import { fetch } from './docker-hub-common-fetch.js'
const buildSchema = Joi.object({
count: nonNegativeInteger.required(),
@@ -20,58 +27,79 @@ const buildSchema = Joi.object({
Joi.object({
digest: Joi.string(),
architecture: Joi.string().required(),
- })
+ }),
),
- })
+ }),
),
}).required()
+const sortEnum = ['date', 'semver']
+
const queryParamSchema = Joi.object({
- sort: Joi.string()
- .valid('date', 'semver')
- .default('date'),
+ sort: Joi.string().valid('date', 'semver').default('date'),
+ arch: archSchema.default('amd64'),
}).required()
-module.exports = class DockerVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
+const openApiQueryParams = queryParams(
+ {
+ name: 'sort',
+ example: 'semver',
+ schema: { type: 'string', enum: sortEnum },
+ description: 'If not specified, the default is `date`',
+ },
+ {
+ name: 'arch',
+ example: 'amd64',
+ schema: { type: 'string', enum: archEnum },
+ description: 'If not specified, the default is `amd64`',
+ },
+)
- static get route() {
- return { ...buildDockerUrl('v', true), queryParamSchema }
+export default class DockerVersion extends BaseJsonService {
+ static category = 'version'
+ static route = { ...buildDockerUrl('v', true), queryParamSchema }
+
+ static auth = {
+ userKey: 'dockerhub_username',
+ passKey: 'dockerhub_pat',
+ authorizedOrigins: [
+ 'https://hub.docker.com',
+ 'https://registry.hub.docker.com',
+ ],
+ isRequired: false,
}
- static get examples() {
- return [
- {
- title: 'Docker Image Version (latest by date)',
- pattern: ':user/:repo',
- namedParams: { user: '_', repo: 'alpine' },
- queryParams: { sort: 'date' },
- staticPreview: this.render({ version: '3.9.5' }),
- },
- {
- title: 'Docker Image Version (latest semver)',
- pattern: ':user/:repo',
- namedParams: { user: '_', repo: 'alpine' },
- queryParams: { sort: 'semver' },
- staticPreview: this.render({ version: '3.11.3' }),
+ static openApi = {
+ '/docker/v/{user}/{repo}': {
+ get: {
+ summary: 'Docker Image Version',
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: '_' },
+ { name: 'repo', example: 'alpine' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Docker Image Version (tag latest semver)',
- pattern: ':user/:repo/:tag',
- namedParams: { user: '_', repo: 'alpine', tag: '3.6' },
- staticPreview: this.render({ version: '3.6.5' }),
+ },
+ '/docker/v/{user}/{repo}/{tag}': {
+ get: {
+ summary: 'Docker Image Version (tag)',
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: '_' },
+ { name: 'repo', example: 'alpine' },
+ { name: 'tag', example: '3.6' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'version',
- color: 'blue',
- }
- }
+ static _cacheLength = 900
+
+ static defaultBadgeData = { label: 'version', color: 'blue' }
static render({ version }) {
return renderVersionBadge({ version })
@@ -79,16 +107,16 @@ module.exports = class DockerVersion extends BaseJsonService {
async fetch({ user, repo, page }) {
page = page ? `&page=${page}` : ''
- return this._requestJson({
+ return await fetch(this, {
schema: buildSchema,
url: `https://registry.hub.docker.com/v2/repositories/${getDockerHubUser(
- user
+ user,
)}/${repo}/tags?page_size=100&ordering=last_updated${page}`,
- errorMessages: { 404: 'repository or tag not found' },
+ httpErrors: { 404: 'repository or tag not found' },
})
}
- transform({ tag, sort, data, pagedData }) {
+ transform({ tag, sort, data, pagedData, arch = 'amd64' }) {
let version
if (!tag && sort === 'date') {
@@ -96,9 +124,7 @@ module.exports = class DockerVersion extends BaseJsonService {
if (version !== 'latest') {
return { version }
}
- const imageTag = data.results[0].images.find(
- i => i.architecture === 'amd64'
- ) // Digest is the unique field that we utilise to match images
+ const imageTag = data.results[0].images.find(i => i.architecture === arch) // Digest is the unique field that we utilise to match images
if (!imageTag) {
throw new InvalidResponse({
prettyMessage: 'digest not found for latest tag',
@@ -107,7 +133,14 @@ module.exports = class DockerVersion extends BaseJsonService {
const { digest } = imageTag
return { version: getDigestSemVerMatches({ data: pagedData, digest }) }
} else if (!tag && sort === 'semver') {
- const matches = data.map(d => d.name)
+ const matches = data
+ .filter(d => d.images.some(image => image.architecture === arch))
+ .map(d => d.name)
+ if (matches.length === 0) {
+ throw new InvalidResponse({
+ prettyMessage: `no images found for arch ${arch}`,
+ })
+ }
return { version: latest(matches) }
} else {
version = data.find(d => d.name === tag)
@@ -117,12 +150,18 @@ module.exports = class DockerVersion extends BaseJsonService {
if (Object.keys(version.images).length === 0) {
return { version: version.name }
}
- const { digest } = version.images.find(i => i.architecture === 'amd64')
+ const image = version.images.find(i => i.architecture === arch)
+ if (!image) {
+ throw new InvalidResponse({
+ prettyMessage: 'digest not found for given tag',
+ })
+ }
+ const { digest } = image
return { version: getDigestSemVerMatches({ data, digest }) }
}
}
- async handle({ user, repo, tag }, { sort }) {
+ async handle({ user, repo, tag }, { sort, arch }) {
let data, pagedData
if (!tag && sort === 'date') {
@@ -145,7 +184,13 @@ module.exports = class DockerVersion extends BaseJsonService {
})
}
- const { version } = await this.transform({ tag, sort, data, pagedData })
+ const { version } = this.transform({
+ tag,
+ sort,
+ data,
+ pagedData,
+ arch,
+ })
return this.constructor.render({ version })
}
}
diff --git a/services/docker/docker-version.spec.js b/services/docker/docker-version.spec.js
index 9be9cd9bc7d30..73c61e4eab85f 100644
--- a/services/docker/docker-version.spec.js
+++ b/services/docker/docker-version.spec.js
@@ -1,17 +1,17 @@
-'use strict'
-
-const { expect } = require('chai')
-const { test, given } = require('sazerac')
-const { InvalidResponse } = require('..')
-const DockerVersion = require('./docker-version.service')
-const {
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import DockerVersion from './docker-version.service.js'
+import {
versionDataNoTagDateSort,
versionPagedDataNoTagDateSort,
versionDataNoTagSemVerSort,
versionDataWithTag,
-} = require('./docker-fixtures')
+ versionDataWithVaryingArchitectures,
+ versionDataWithArchSpecificVersions,
+} from './docker-fixtures.js'
-describe('DockerVersion', function() {
+describe('DockerVersion', function () {
test(DockerVersion.prototype.transform, () => {
given({
tag: '',
@@ -48,9 +48,34 @@ describe('DockerVersion', function() {
}).expect({
version: '3.10.4',
})
+
+ // tag & custom architecture
+ given({
+ tag: '3.9',
+ data: versionDataWithVaryingArchitectures,
+ arch: 'arm',
+ }).expect({
+ version: '3.9',
+ })
+
+ // sort & custom architecture
+ given({
+ data: versionDataWithVaryingArchitectures,
+ sort: 'semver',
+ arch: 'ppc64le',
+ }).expect({
+ version: '3.10.4',
+ })
+ given({
+ data: versionDataWithArchSpecificVersions,
+ sort: 'semver',
+ arch: 'arm64',
+ }).expect({
+ version: '3.9-arm64',
+ })
})
- it('throws InvalidResponse error with latest tag and no amd64 architecture digests', function() {
+ it('throws InvalidResponse error with latest tag and no amd64 architecture digests', function () {
expect(() => {
DockerVersion.prototype.transform({
sort: 'date',
@@ -78,4 +103,80 @@ describe('DockerVersion', function() {
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'digest not found for latest tag')
})
+
+ // https://github.com/badges/shields/issues/5535
+ it('throws InvalidResponse error with custom tag and no amd64 architecture digests', function () {
+ expect(() => {
+ DockerVersion.prototype.transform({
+ tag: '3.10',
+ data: [
+ {
+ name: '3.10',
+ images: [
+ {
+ architecture: 'arm64',
+ digest:
+ 'sha256:597bd5c319cc09d6bb295b4ef23cac50ec7c373fff5fe923cfd246ec09967b31',
+ },
+ {
+ architecture: 'arm',
+ digest:
+ 'sha256:c5ea49127cd44d0f50eafda229a056bb83b6e691883c56fd863d42675fae3909',
+ },
+ ],
+ },
+ ],
+ })
+ })
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'digest not found for given tag')
+ })
+
+ it('throws InvalidResponse error with custom tag and no matching architecture digests', function () {
+ expect(() => {
+ DockerVersion.prototype.transform({
+ tag: '3.10',
+ arch: '386',
+ data: [
+ {
+ name: '3.9',
+ images: [
+ {
+ digest:
+ 'sha256:ab3fe83c0696e3f565c9b4a734ec309ae9bd0d74c192de4590fd6dc2ef717815',
+ architecture: 'amd64',
+ },
+ {
+ digest:
+ 'sha256:c7b3e8392e08c971e98627e2bddd10c7fa9d2eae797a16bc94de9709bb9300d0',
+ architecture: '386',
+ },
+ {
+ digest:
+ 'sha256:5292cebaf695db860087c5582d340a406613891b2819092747b0388da47936c8',
+ architecture: 'arm',
+ },
+ ],
+ },
+ {
+ name: '3.10',
+ images: [
+ {
+ architecture: 'arm',
+ digest:
+ 'sha256:c5ea49127cd44d0f50eafda229a056bb83b6e691883c56fd863d42675fae3909',
+ },
+ {
+ architecture: 'arm64',
+ digest:
+ 'sha256:597bd5c319cc09d6bb295b4ef23cac50ec7c373fff5fe923cfd246ec09967b31',
+ },
+ ],
+ },
+ ],
+ })
+ })
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'digest not found for given tag')
+ })
})
diff --git a/services/docker/docker-version.tester.js b/services/docker/docker-version.tester.js
index 011a66110d696..8410a69ffb060 100644
--- a/services/docker/docker-version.tester.js
+++ b/services/docker/docker-version.tester.js
@@ -1,34 +1,33 @@
-'use strict'
-
-const { isSemVer } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('docker version (valid, library)')
- .get('/_/alpine.json')
+ .get('/docker/example-voting-app-vote.json')
.expectBadge({
label: 'version',
- message: isSemVer,
+ message: 'latest',
})
t.create('docker version (valid, library with tag)')
.get('/_/alpine/latest.json')
.expectBadge({
label: 'version',
- message: isSemVer,
+ message: isSemver,
})
t.create('docker version (valid, user)')
- .get('/jrottenberg/ffmpeg.json')
+ .get('/datadog/dogstatsd.json')
.expectBadge({
label: 'version',
- message: isSemVer,
+ message: isSemver,
})
t.create('docker version (valid, user with tag)')
.get('/jrottenberg/ffmpeg/3.2-alpine.json')
.expectBadge({
label: 'version',
- message: isSemVer,
+ message: isSemver,
})
t.create('docker version (invalid, incorrect tag)')
@@ -42,5 +41,5 @@ t.create('docker version (invalid, unknown repository)')
.get('/_/not-a-real-repo.json')
.expectBadge({
label: 'version',
- message: 'repository not found',
+ message: 'repository or tag not found',
})
diff --git a/services/docsrs/docsrs.service.js b/services/docsrs/docsrs.service.js
new file mode 100644
index 0000000000000..f01b32906a72d
--- /dev/null
+++ b/services/docsrs/docsrs.service.js
@@ -0,0 +1,72 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ doc_status: Joi.boolean().required(),
+}).required()
+
+export default class DocsRs extends BaseJsonService {
+ static category = 'build'
+ static route = { base: 'docsrs', pattern: ':crate/:version?' }
+ static openApi = {
+ '/docsrs/{crate}/{version}': {
+ get: {
+ summary: 'docs.rs (with version)',
+ parameters: pathParams(
+ {
+ name: 'crate',
+ example: 'regex',
+ },
+ {
+ name: 'version',
+ example: 'latest',
+ },
+ ),
+ },
+ },
+ '/docsrs/{crate}': {
+ get: {
+ summary: 'docs.rs',
+ parameters: pathParams({
+ name: 'crate',
+ example: 'regex',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'docs' }
+
+ static render({ docStatus, version }) {
+ let label = `docs@${version}`
+ if (version === 'latest') {
+ label = 'docs'
+ }
+ if (docStatus) {
+ return {
+ label,
+ message: 'passing',
+ color: 'success',
+ }
+ } else {
+ return {
+ label,
+ message: 'failing',
+ color: 'critical',
+ }
+ }
+ }
+
+ async fetch({ crate, version }) {
+ return await this._requestJson({
+ schema,
+ url: `https://docs.rs/crate/${crate}/${version}/status.json`,
+ httpErrors: version ? { 400: 'malformed version' } : {},
+ })
+ }
+
+ async handle({ crate, version = 'latest' }) {
+ const { doc_status: docStatus } = await this.fetch({ crate, version })
+ return this.constructor.render({ version, docStatus })
+ }
+}
diff --git a/services/docsrs/docsrs.tester.js b/services/docsrs/docsrs.tester.js
new file mode 100644
index 0000000000000..715ec413b9439
--- /dev/null
+++ b/services/docsrs/docsrs.tester.js
@@ -0,0 +1,49 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Docs with no version specified')
+ .get('/tokio.json')
+ .expectBadge({
+ label: 'docs',
+ message: Joi.equal('passing', 'failing'),
+ })
+
+t.create('Passing docs for version').get('/tokio/1.37.0.json').expectBadge({
+ label: 'docs@1.37.0',
+ message: 'passing',
+ color: 'brightgreen',
+})
+
+t.create('Failing docs for version').get('/tokio/1.32.1.json').expectBadge({
+ label: 'docs@1.32.1',
+ message: 'failing',
+ color: 'red',
+})
+
+t.create('Multiple builds, latest passing')
+ .get('/bevy_tweening/0.3.1.json')
+ .expectBadge({
+ label: 'docs@0.3.1',
+ message: 'passing',
+ color: 'brightgreen',
+ })
+
+t.create('Getting latest version works')
+ .get('/rand/latest.json')
+ .expectBadge({
+ label: 'docs',
+ message: Joi.equal('passing', 'failing'),
+ })
+
+t.create('Crate not found')
+ .get('/not-a-crate/latest.json')
+ .expectBadge({ label: 'docs', message: 'not found' })
+
+t.create('Version not found')
+ .get('/tokio/0.8.json')
+ .expectBadge({ label: 'docs', message: 'not found' })
+
+t.create('Malformed version')
+ .get('/tokio/not-a-version.json')
+ .expectBadge({ label: 'docs', message: 'malformed version' })
diff --git a/services/dotnetstatus/dotnetstatus.service.js b/services/dotnetstatus/dotnetstatus.service.js
deleted file mode 100644
index 11a7918cc9801..0000000000000
--- a/services/dotnetstatus/dotnetstatus.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'dependencies',
- route: {
- base: 'dotnetstatus',
- pattern: ':various+',
- },
- label: 'dotnet status',
- dateAdded: new Date('2018-04-01'),
-})
diff --git a/services/dotnetstatus/dotnetstatus.tester.js b/services/dotnetstatus/dotnetstatus.tester.js
deleted file mode 100644
index e2b0ea1b33520..0000000000000
--- a/services/dotnetstatus/dotnetstatus.tester.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'dotnetstatus',
- title: 'dotnet-status',
-}))
-
-t.create('no longer available (previously get package status)')
- .get('/gh/jaredcnance/dotnet-status/API.json')
- .expectBadge({
- label: 'dotnet status',
- message: 'no longer available',
- })
diff --git a/services/downloads.js b/services/downloads.js
new file mode 100644
index 0000000000000..f2fb0690726ee
--- /dev/null
+++ b/services/downloads.js
@@ -0,0 +1,56 @@
+/**
+ * @module
+ */
+
+import { downloadCount } from './color-formatters.js'
+import { metric } from './text-formatters.js'
+
+/**
+ * Handles rendering concerns of badges that display
+ * download counts, with override/customization support
+ *
+ * @param {object} attrs Refer to individual attrs
+ * @param {number} attrs.downloads Number of downloads
+ * @param {string} [attrs.interval] Period or interval the downloads occurred
+ * (e.g. day, week, month). If provided then this Will be reflected
+ * in the badge message unless overridden by other message-related parameters
+ * @param {string} [attrs.version] Version or tag that was downloaded
+ * which will be reflected in the badge label (unless the label is overridden)
+ * @param {string} [attrs.labelOverride] If provided then the badge label is set to this
+ * value overriding any other label-related parameters
+ * @param {string} [attrs.colorOverride] If provided then the badge color is set to this
+ * value instead of the color being based on the count of downloads
+ * @param {string} [attrs.messageSuffixOverride] If provided then the badge message will
+ * will have this value added to the download count, separated with a space
+ * @returns {object} Badge
+ */
+function renderDownloadsBadge({
+ downloads,
+ interval,
+ version,
+ labelOverride,
+ colorOverride,
+ messageSuffixOverride,
+}) {
+ let messageSuffix = ''
+ if (messageSuffixOverride) {
+ messageSuffix = ` ${messageSuffixOverride}`
+ } else if (interval) {
+ messageSuffix = `/${interval}`
+ }
+
+ let label
+ if (labelOverride) {
+ label = labelOverride
+ } else if (version) {
+ label = `downloads@${version}`
+ }
+
+ return {
+ label,
+ color: colorOverride || downloadCount(downloads),
+ message: `${metric(downloads)}${messageSuffix}`,
+ }
+}
+
+export { renderDownloadsBadge }
diff --git a/services/downloads.spec.js b/services/downloads.spec.js
new file mode 100644
index 0000000000000..bf29274f82964
--- /dev/null
+++ b/services/downloads.spec.js
@@ -0,0 +1,43 @@
+import { test, given } from 'sazerac'
+import { renderDownloadsBadge } from './downloads.js'
+import { downloadCount } from './color-formatters.js'
+import { metric } from './text-formatters.js'
+
+const downloads = 2345
+const message = metric(downloads)
+const color = downloadCount(downloads)
+
+describe('downloads', function () {
+ test(renderDownloadsBadge, () => {
+ given({ downloads }).expect({ label: undefined, color, message })
+ given({ downloads, labelOverride: 'recent downloads' }).expect({
+ label: 'recent downloads',
+ color,
+ message,
+ })
+ given({ downloads, version: 'v1.0.0' }).expect({
+ label: 'downloads@v1.0.0',
+ color,
+ message,
+ })
+ given({
+ downloads,
+ messageSuffixOverride: '[foo.tar.gz]',
+ interval: 'week',
+ }).expect({
+ label: undefined,
+ color,
+ message: `${message} [foo.tar.gz]`,
+ })
+ given({ downloads, interval: 'year' }).expect({
+ label: undefined,
+ color,
+ message: `${message}/year`,
+ })
+ given({ downloads, colorOverride: 'pink' }).expect({
+ label: undefined,
+ color: 'pink',
+ message,
+ })
+ })
+})
diff --git a/services/drone/drone-build.service.js b/services/drone/drone-build.service.js
index b89fe14a50bff..cda006a1b3018 100644
--- a/services/drone/drone-build.service.js
+++ b/services/drone/drone-build.service.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { optionalUrl } from '../validators.js'
+import { BaseJsonService, queryParam, pathParam } from '../index.js'
const schema = Joi.object({
status: Joi.alternatives()
@@ -15,73 +13,62 @@ const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class DroneBuild extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- queryParamSchema,
- base: 'drone/build',
- pattern: ':user/:repo/:branch*',
- }
+export default class DroneBuild extends BaseJsonService {
+ static category = 'build'
+ static route = {
+ base: 'drone/build',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
}
- static get auth() {
- return { passKey: 'drone_token', serviceKey: 'drone' }
- }
+ static auth = { passKey: 'drone_token', serviceKey: 'drone' }
- static get examples() {
- return [
- {
- title: 'Drone (cloud)',
- pattern: ':user/:repo',
- namedParams: {
- user: 'drone',
- repo: 'drone',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
+ static openApi = {
+ '/drone/build/{user}/{repo}': {
+ get: {
+ summary: 'Drone',
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'drone',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'autoscaler',
+ }),
+ queryParam({
+ name: 'server',
+ example: 'https://drone.shields.io',
+ }),
+ ],
},
- {
- title: 'Drone (cloud) with branch',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'drone',
- repo: 'drone',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
+ },
+ '/drone/build/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Drone (branch)',
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'drone',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'autoscaler',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ queryParam({
+ name: 'server',
+ example: 'https://drone.shields.io',
+ }),
+ ],
},
- {
- title: 'Drone (self-hosted)',
- pattern: ':user/:repo',
- queryParams: { server: 'https://drone.shields.io' },
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
- },
- {
- title: 'Drone (self-hosted) with branch',
- pattern: ':user/:repo/:branch',
- queryParams: { server: 'https://drone.shields.io' },
- namedParams: {
- user: 'badges',
- repo: 'shields',
- branch: 'feat/awesome-thing',
- },
- staticPreview: renderBuildStatusBadge({ status: 'success' }),
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
- }
+ static defaultBadgeData = { label: 'build' }
async handle({ user, repo, branch }, { server = 'https://cloud.drone.io' }) {
const json = await this._requestJson(
@@ -89,12 +76,12 @@ module.exports = class DroneBuild extends BaseJsonService {
schema,
url: `${server}/api/repos/${user}/${repo}/builds/latest`,
options: {
- qs: { ref: branch ? `refs/heads/${branch}` : undefined },
+ searchParams: { ref: branch ? `refs/heads/${branch}` : undefined },
},
- errorMessages: {
+ httpErrors: {
401: 'repo not found or not authorized',
},
- })
+ }),
)
return renderBuildStatusBadge({ status: json.status })
}
diff --git a/services/drone/drone-build.spec.js b/services/drone/drone-build.spec.js
index 06837dbad1f28..d14812cc86d81 100644
--- a/services/drone/drone-build.spec.js
+++ b/services/drone/drone-build.spec.js
@@ -1,18 +1,16 @@
-'use strict'
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import DroneBuild from './drone-build.service.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const DroneBuild = require('./drone-build.service')
-
-describe('DroneBuild', function() {
+describe('DroneBuild', function () {
cleanUpNockAfterEach()
- it('Sends auth headers to cloud instance', async function() {
+ it('Sends auth headers to cloud instance', async function () {
const token = 'abc123'
const scope = nock('https://cloud.drone.io', {
- reqheaders: { Authorization: `Bearer abc123` },
+ reqheaders: { Authorization: 'Bearer abc123' },
})
.get(/.*/)
.reply(200, { status: 'passing' })
@@ -32,8 +30,8 @@ describe('DroneBuild', function() {
drone_token: token,
},
},
- { user: 'atlassian', repo: 'python-bitbucket' }
- )
+ { user: 'atlassian', repo: 'python-bitbucket' },
+ ),
).to.deep.equal({
label: undefined,
message: 'passing',
diff --git a/services/drone/drone-build.tester.js b/services/drone/drone-build.tester.js
index d00f477c280e6..125d656d6dfa7 100644
--- a/services/drone/drone-build.tester.js
+++ b/services/drone/drone-build.tester.js
@@ -1,24 +1,23 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isDroneBuildStatus = Joi.alternatives().try(
isBuildStatus,
Joi.equal('none'),
- Joi.equal('killed')
+ Joi.equal('killed'),
)
t.create('cloud-hosted build status on default branch')
- .get('/drone/drone.json')
+ .get('/drone/autoscaler.json')
.expectBadge({
label: 'build',
message: isDroneBuildStatus,
})
t.create('cloud-hosted build status on named branch')
- .get('/drone/drone/master.json')
+ .get('/drone/autoscaler/master.json')
.expectBadge({
label: 'build',
message: isDroneBuildStatus,
@@ -36,7 +35,7 @@ t.create('self-hosted build status on default branch')
.intercept(nock =>
nock('https://drone.shields.io/api/repos')
.get('/badges/shields/builds/latest')
- .reply(200, { status: 'success' })
+ .reply(200, { status: 'success' }),
)
.expectBadge({
label: 'build',
@@ -45,13 +44,13 @@ t.create('self-hosted build status on default branch')
t.create('self-hosted build status on named branch')
.get(
- '/badges/shields/feat/awesome-thing.json?server=https://drone.shields.io'
+ '/badges/shields/feat/awesome-thing.json?server=https://drone.shields.io',
)
.intercept(nock =>
nock('https://drone.shields.io/api/repos')
.get('/badges/shields/builds/latest')
.query({ ref: 'refs/heads/feat/awesome-thing' })
- .reply(200, { status: 'success' })
+ .reply(200, { status: 'success' }),
)
.expectBadge({
label: 'build',
diff --git a/services/dub/dub-download.service.js b/services/dub/dub-download.service.js
index 1fff5668273d3..1afe83180b010 100644
--- a/services/dub/dub-download.service.js
+++ b/services/dub/dub-download.service.js
@@ -1,10 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
downloads: Joi.object({
@@ -18,82 +15,80 @@ const schema = Joi.object({
const intervalMap = {
dd: {
transform: json => json.downloads.daily,
- messageSuffix: '/day',
+ interval: 'day',
},
dw: {
transform: json => json.downloads.weekly,
- messageSuffix: '/week',
+ interval: 'week',
},
dm: {
transform: json => json.downloads.monthly,
- messageSuffix: '/month',
+ interval: 'month',
},
dt: {
transform: json => json.downloads.total,
- messageSuffix: '',
+ interval: '',
},
}
-module.exports = class DubDownloads extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'dub',
- pattern: ':interval(dd|dw|dm|dt)/:packageName/:version*',
- }
+export default class DubDownloads extends BaseJsonService {
+ static category = 'downloads'
+ static route = {
+ base: 'dub',
+ pattern: ':interval(dd|dw|dm|dt)/:packageName/:version*',
}
- static get examples() {
- return [
- {
- title: 'DUB',
- namedParams: { interval: 'dm', packageName: 'vibe-d' },
- staticPreview: this.render({ interval: 'dm', downloadCount: 5000 }),
+ static openApi = {
+ '/dub/{interval}/{packageName}': {
+ get: {
+ summary: 'DUB Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Weekly, Monthly, or Total downloads',
+ },
+ {
+ name: 'packageName',
+ example: 'vibe-d',
+ },
+ ),
},
- {
- title: 'DUB (version)',
- namedParams: {
- interval: 'dm',
- packageName: 'vibe-d',
- version: '0.8.4',
- },
- staticPreview: this.render({
- interval: 'dm',
- version: '0.8.4',
- downloadCount: 100,
- }),
+ },
+ '/dub/{interval}/{packageName}/{version}': {
+ get: {
+ summary: 'DUB Downloads (specific version)',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Weekly, Monthly, or Total downloads',
+ },
+ {
+ name: 'packageName',
+ example: 'vibe-d',
+ },
+ {
+ name: 'version',
+ description:
+ 'This can either be a numeric version like `0.8.4` or the string `latest`',
+ example: '0.8.4',
+ },
+ ),
},
- {
- title: 'DUB (latest)',
- namedParams: {
- interval: 'dm',
- packageName: 'vibe-d',
- version: 'latest',
- },
- staticPreview: this.render({
- interval: 'dm',
- version: 'latest',
- downloadCount: 100,
- }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'downloads' }
+ },
}
- static render({ interval, version, downloadCount }) {
- const { messageSuffix } = intervalMap[interval]
+ static defaultBadgeData = { label: 'downloads' }
- return {
- label: version ? `downloads@${version}` : 'downloads',
- message: `${metric(downloadCount)}${messageSuffix}`,
- color: downloadCountColor(downloadCount),
- }
+ static render({ interval, version, downloads }) {
+ return renderDownloadsBadge({
+ downloads,
+ version,
+ interval: intervalMap[interval].interval,
+ })
}
async fetch({ packageName, version }) {
@@ -109,7 +104,7 @@ module.exports = class DubDownloads extends BaseJsonService {
const { transform } = intervalMap[interval]
const json = await this.fetch({ packageName, version })
- const downloadCount = transform(json)
- return this.constructor.render({ interval, downloadCount, version })
+ const downloads = transform(json)
+ return this.constructor.render({ interval, downloads, version })
}
}
diff --git a/services/dub/dub-download.tester.js b/services/dub/dub-download.tester.js
index a324ed0e4bc2f..aa16978372403 100644
--- a/services/dub/dub-download.tester.js
+++ b/services/dub/dub-download.tester.js
@@ -1,24 +1,21 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isDownloadsColor = Joi.equal(
'red',
'yellow',
'yellowgreen',
'green',
- 'brightgreen'
+ 'brightgreen',
)
-t.create('total downloads (valid)')
- .get('/dt/vibe-d.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- color: isDownloadsColor,
- })
+t.create('total downloads (valid)').get('/dt/vibe-d.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+ color: isDownloadsColor,
+})
t.create('total downloads, specific version (valid)')
.get('/dt/dub/1.16.0.json')
@@ -37,29 +34,23 @@ t.create('total downloads, latest version (valid)')
color: isDownloadsColor,
})
-t.create('daily downloads (valid)')
- .get('/dd/vibe-d.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- color: isDownloadsColor,
- })
-
-t.create('weekly downloads (valid)')
- .get('/dw/vibe-d.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- color: isDownloadsColor,
- })
-
-t.create('monthly downloads (valid)')
- .get('/dm/vibe-d.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- color: isDownloadsColor,
- })
+t.create('daily downloads (valid)').get('/dd/vibe-d.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: isDownloadsColor,
+})
+
+t.create('weekly downloads (valid)').get('/dw/vibe-d.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: isDownloadsColor,
+})
+
+t.create('monthly downloads (valid)').get('/dm/vibe-d.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: isDownloadsColor,
+})
t.create('total downloads (not found)')
.get('/dt/not-a-package.json')
diff --git a/services/dub/dub-license.service.js b/services/dub/dub-license.service.js
index 3a68c843f73a8..f474b33c4bc1f 100644
--- a/services/dub/dub-license.service.js
+++ b/services/dub/dub-license.service.js
@@ -1,38 +1,27 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderLicenseBadge } from '../licenses.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
info: Joi.object({ license: Joi.string().required() }).required(),
})
-module.exports = class DubLicense extends BaseJsonService {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'dub/l',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'DUB',
- namedParams: { packageName: 'vibe-d' },
- staticPreview: renderLicenseBadge({ licenses: ['MIT'] }),
+export default class DubLicense extends BaseJsonService {
+ static category = 'license'
+ static route = { base: 'dub/l', pattern: ':packageName' }
+ static openApi = {
+ '/dub/l/{packageName}': {
+ get: {
+ summary: 'DUB License',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vibe-d',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
async fetch({ packageName }) {
return this._requestJson({
diff --git a/services/dub/dub-license.tester.js b/services/dub/dub-license.tester.js
index f40797ac530f0..0da04e2a2411f 100644
--- a/services/dub/dub-license.tester.js
+++ b/services/dub/dub-license.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license (valid)')
.get('/vibe-d.json')
diff --git a/services/dub/dub-score.service.js b/services/dub/dub-score.service.js
new file mode 100644
index 0000000000000..5422b35192fd7
--- /dev/null
+++ b/services/dub/dub-score.service.js
@@ -0,0 +1,46 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+
+const schema = Joi.object({
+ score: Joi.number().required(),
+})
+
+export default class DubScore extends BaseJsonService {
+ static category = 'rating'
+
+ static route = { base: 'dub/score', pattern: ':packageName' }
+
+ static openApi = {
+ '/dub/score/{packageName}': {
+ get: {
+ summary: 'DUB Score',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vibe-d',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'score' }
+
+ static render({ score }) {
+ return {
+ label: 'score',
+ color: colorScale([1, 2, 3, 4, 5])(score),
+ message: score,
+ }
+ }
+
+ async fetch({ packageName }) {
+ const url = `https://code.dlang.org/api/packages/${packageName}/stats`
+ return this._requestJson({ schema, url })
+ }
+
+ async handle({ packageName }) {
+ let { score } = await this.fetch({ packageName })
+ score = score.toFixed(1)
+ return this.constructor.render({ score })
+ }
+}
diff --git a/services/dub/dub-score.tester.js b/services/dub/dub-score.tester.js
new file mode 100644
index 0000000000000..29d09a706914a
--- /dev/null
+++ b/services/dub/dub-score.tester.js
@@ -0,0 +1,25 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+const isScoreColor = Joi.equal(
+ 'red',
+ 'orange',
+ 'yellow',
+ 'yellowgreen',
+ 'green',
+ 'brightgreen',
+)
+
+t.create('version (valid)')
+ .get('/vibe-d.json')
+ .expectBadge({
+ label: 'score',
+ message: Joi.number().min(0).max(5),
+ color: isScoreColor,
+ })
+
+t.create('version (not found)')
+ .get('/not-a-package.json')
+ .expectBadge({ label: 'score', message: 'not found' })
diff --git a/services/dub/dub-version.service.js b/services/dub/dub-version.service.js
index 33cbb90454a1f..2b1cd1eb4d859 100644
--- a/services/dub/dub-version.service.js
+++ b/services/dub/dub-version.service.js
@@ -1,36 +1,25 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.string().required()
-module.exports = class DubVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'dub/v',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'DUB',
- namedParams: { packageName: 'vibe-d' },
- staticPreview: renderVersionBadge({ version: 'v0.8.4' }),
+export default class DubVersion extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'dub/v', pattern: ':packageName' }
+ static openApi = {
+ '/dub/v/{packageName}': {
+ get: {
+ summary: 'DUB Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vibe-d',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'dub' }
- }
+ static defaultBadgeData = { label: 'dub' }
async fetch({ packageName }) {
return this._requestJson({
diff --git a/services/dub/dub-version.tester.js b/services/dub/dub-version.tester.js
index 26824bdee41c5..88015d7660e5a 100644
--- a/services/dub/dub-version.tester.js
+++ b/services/dub/dub-version.tester.js
@@ -1,10 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('version (valid)')
.get('/vibe-d.json')
diff --git a/services/dynamic-common.js b/services/dynamic-common.js
index 80e25754fcb28..c1be2faf2bb5a 100644
--- a/services/dynamic-common.js
+++ b/services/dynamic-common.js
@@ -1,26 +1,54 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to dynamic badges.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
-const toArray = require('../core/base-service/to-array')
-const validate = require('../core/base-service/validate')
-const { InvalidResponse } = require('.')
+import Joi from 'joi'
+import toArray from '../core/base-service/to-array.js'
+import validate from '../core/base-service/validate.js'
+import { InvalidResponse } from './index.js'
-const errorMessages = {
+/**
+ * Map of error codes and their corresponding error messages.
+ *
+ * @type {object}
+ */
+const httpErrors = {
404: 'resource not found',
}
+/**
+ * Joi schema for validating individual value.
+ * Checks if the individual value is of type string or number.
+ *
+ * @type {Joi}
+ */
const individualValueSchema = Joi.alternatives()
.try(Joi.string(), Joi.number())
.required()
+/**
+ * Joi schema for validating compound value.
+ * Checks if the compound value is of type individualValueSchema, array of individualValueSchema or empty array.
+ *
+ * @type {Joi}
+ */
const compoundValueSchema = Joi.alternatives().try(
individualValueSchema,
- Joi.array()
- .items(individualValueSchema)
- .required(),
- Joi.array().length(0)
+ Joi.array().items(individualValueSchema).required(),
+ Joi.array().length(0),
)
+/**
+ * Look up the value in the data object by key and validate the value against compoundValueSchema.
+ *
+ * @param {object} attrs Refer to individual attributes
+ * @param {object} attrs.data Object containing the data for validation
+ * @param {string} attrs.key Key to retrieve the data from object for validation
+ * @throws {InvalidResponse|Error} Error if Joi validation fails due to invalid or no schema
+ * @returns {object} Value if Joi validation is success
+ */
function transformAndValidate({ data, key }) {
return validate(
{
@@ -30,10 +58,24 @@ function transformAndValidate({ data, key }) {
traceSuccessMessage: 'Key value after validation',
},
data[key],
- compoundValueSchema
+ compoundValueSchema,
)
}
+/**
+ * Handles rendering concerns of dynamic badges.
+ * Determines the label of the badge according to the tag and defaultLabel.
+ * Determines the message of the badge according to the prefix, suffix and value.
+ * Sets the color of the badge to blue.
+ *
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} attrs.defaultLabel default badge label
+ * @param {string} [attrs.tag] If provided then this value will be appended to the badge label, e.g. `foobar@v1.23`
+ * @param {any} attrs.value Value or array of value to be used for the badge message
+ * @param {string} [attrs.prefix] If provided then the badge message will use this value as a prefix
+ * @param {string} [attrs.suffix] If provided then the badge message will use this value as a suffix
+ * @returns {object} Badge with label, message and color properties
+ */
function renderDynamicBadge({
defaultLabel,
tag,
@@ -50,8 +92,8 @@ function renderDynamicBadge({
}
}
-module.exports = {
- errorMessages,
+export {
+ httpErrors,
individualValueSchema,
transformAndValidate,
renderDynamicBadge,
diff --git a/services/dynamic/dynamic-helpers.js b/services/dynamic/dynamic-helpers.js
index ceddf69f747dd..269d681128803 100644
--- a/services/dynamic/dynamic-helpers.js
+++ b/services/dynamic/dynamic-helpers.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
+import Joi from 'joi'
+import { url } from '../validators.js'
const queryParamSchema = Joi.object({
- url: optionalUrl.required(),
+ url,
query: Joi.string().required(),
prefix: Joi.alternatives().try(Joi.string(), Joi.number()),
suffix: Joi.alternatives().try(Joi.string(), Joi.number()),
@@ -20,6 +18,4 @@ function createRoute(which) {
}
}
-module.exports = {
- createRoute,
-}
+export { createRoute }
diff --git a/services/dynamic/dynamic-json.service.js b/services/dynamic/dynamic-json.service.js
index 2d9ced69e96be..45eeaeed528e2 100644
--- a/services/dynamic/dynamic-json.service.js
+++ b/services/dynamic/dynamic-json.service.js
@@ -1,24 +1,58 @@
-'use strict'
+import { MetricNames } from '../../core/base-service/metric-helper.js'
+import { BaseJsonService, queryParams } from '../index.js'
+import { createRoute } from './dynamic-helpers.js'
+import jsonPath from './json-path.js'
-const { MetricNames } = require('../../core/base-service/metric-helper')
-const { BaseJsonService } = require('..')
-const { createRoute } = require('./dynamic-helpers')
-const jsonPath = require('./json-path')
+const description = `
+The Dynamic JSON Badge allows you to extract an arbitrary value from any
+JSON Document using a JSONPath selector and show it on a badge.
+`
-module.exports = class DynamicJson extends jsonPath(BaseJsonService) {
- static get enabledMetrics() {
- return [MetricNames.SERVICE_RESPONSE_SIZE]
+export default class DynamicJson extends jsonPath(BaseJsonService) {
+ static enabledMetrics = [MetricNames.SERVICE_RESPONSE_SIZE]
+ static route = createRoute('json')
+ static openApi = {
+ '/badge/dynamic/json': {
+ get: {
+ summary: 'Dynamic JSON Badge',
+ description,
+ parameters: queryParams(
+ {
+ name: 'url',
+ description: 'The URL to a JSON document',
+ required: true,
+ example:
+ 'https://github.com/badges/shields/raw/master/package.json',
+ },
+ {
+ name: 'query',
+ description:
+ 'A JSONPath expression that will be used to query the document',
+ required: true,
+ example: '$.name',
+ },
+ {
+ name: 'prefix',
+ description: 'Optional prefix to append to the value',
+ example: '[',
+ },
+ {
+ name: 'suffix',
+ description: 'Optional suffix to append to the value',
+ example: ']',
+ },
+ ),
+ },
+ },
}
- static get route() {
- return createRoute('json')
- }
-
- async fetch({ schema, url, errorMessages }) {
+ async fetch({ schema, url, httpErrors }) {
return this._requestJson({
schema,
url,
- errorMessages,
+ httpErrors,
+ logErrors: [],
+ options: { timeout: { request: 3500 } },
})
}
}
diff --git a/services/dynamic/dynamic-json.tester.js b/services/dynamic/dynamic-json.tester.js
index 562d9af4dd24a..a033f907a6d8b 100644
--- a/services/dynamic/dynamic-json.tester.js
+++ b/services/dynamic/dynamic-json.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { expect } = require('chai')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { expect } from 'chai'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('No URL specified')
.get('.json?query=$.name&label=Package Name')
@@ -14,7 +13,7 @@ t.create('No URL specified')
t.create('No query specified')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&label=Package Name'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&label=Package Name',
)
.expectBadge({
label: 'Package Name',
@@ -24,7 +23,7 @@ t.create('No query specified')
t.create('Malformed url')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/%0package.json&query=$.name&label=Package Name'
+ '.json?url=https://github.com/badges/shields/raw/master/%0package.json&query=$.name&label=Package Name',
)
.expectBadge({
label: 'Package Name',
@@ -34,7 +33,7 @@ t.create('Malformed url')
t.create('JSON from url')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name',
)
.expectBadge({
label: 'custom badge',
@@ -44,7 +43,7 @@ t.create('JSON from url')
t.create('support uri query parameter')
.get(
- '.json?uri=https://github.com/badges/shields/raw/master/package.json&query=$.name'
+ '.json?uri=https://github.com/badges/shields/raw/master/package.json&query=$.name',
)
.expectBadge({
label: 'custom badge',
@@ -54,13 +53,13 @@ t.create('support uri query parameter')
t.create('multiple results')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$..keywords[0:2:1]'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$..keywords[0:2:1]',
)
.expectBadge({ label: 'custom badge', message: 'GitHub, badge' })
t.create('caching with new query params')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version',
)
.expectBadge({
label: 'custom badge',
@@ -69,7 +68,7 @@ t.create('caching with new query params')
t.create('prefix & suffix & label')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version&prefix=v&suffix= dev&label=Shields'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version&prefix=v&suffix= dev&label=Shields',
)
.expectBadge({
label: 'Shields',
@@ -78,7 +77,7 @@ t.create('prefix & suffix & label')
t.create("key doesn't exist")
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.does_not_exist'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.does_not_exist',
)
.expectBadge({
label: 'custom badge',
@@ -88,7 +87,7 @@ t.create("key doesn't exist")
t.create('invalid url')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version'
+ '.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version',
)
.expectBadge({
label: 'custom badge',
@@ -98,7 +97,7 @@ t.create('invalid url')
t.create('user color overrides default')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name&color=10ADED'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name&color=10ADED',
)
.expectBadge({
label: 'custom badge',
@@ -108,7 +107,7 @@ t.create('user color overrides default')
t.create('error color overrides default')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version'
+ '.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version',
)
.expectBadge({
label: 'custom badge',
@@ -130,10 +129,10 @@ t.create('request should set Accept header')
.intercept(nock =>
nock('https://json-test')
.get('/api.json')
- .reply(200, function(uri, requestBody) {
+ .reply(200, function (uri, requestBody) {
headers = this.req.headers
return '{"name":"test"}'
- })
+ }),
)
.expectBadge({ label: 'custom badge', message: 'test' })
.after(() => {
@@ -142,30 +141,72 @@ t.create('request should set Accept header')
t.create('query with lexical error')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$[?'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$[?',
)
.expectBadge({
label: 'custom badge',
- message: 'unparseable jsonpath query',
- color: 'red',
+ message: 'no result',
+ color: 'lightgrey',
})
t.create('query with parse error')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.foo,'
+ '.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.foo,',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'no result',
+ color: 'lightgrey',
+ })
+
+// Example from https://stackoverflow.com/q/11670384/893113
+const invalidTokenQuery =
+ "$[?(en|**|(@.object.property.one=='other') && (@.object.property.two=='something(abc/def)'))]"
+t.create('query with invalid token')
+ .get(
+ `.json?url=https://github.com/badges/shields/raw/master/package.json&query=${encodeURIComponent(
+ invalidTokenQuery,
+ )}`,
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'query not supported',
+ color: 'red',
+ })
+
+const invalidEscapequery = "$.definitions.common.properties.['@id'].format"
+t.create('query with invalid escape')
+ .get(
+ `.json?url=https://raw.githubusercontent.com/json-ld/json-ld.org/refs/heads/main/schemas/jsonld-schema.json&query=${encodeURIComponent(
+ invalidEscapequery,
+ )}`,
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'query not supported',
+ color: 'red',
+ })
+
+/*
+Based on https://github.com/JSONPath-Plus/JSONPath/blob/v9.0.0/test/test.errors.js#L53-L68
+This functionality is disabled for security reasons.
+*/
+t.create('query with eval filtering expression')
+ .get(
+ `.json?url=https://github.com/badges/shields/raw/master/package.json&query=${encodeURIComponent(
+ '$..keywords[(@.length-1)]',
+ )}`,
)
.expectBadge({
label: 'custom badge',
- message: 'unparseable jsonpath query',
+ message: 'query not supported',
color: 'red',
})
t.create('JSON contains an array')
.get('.json?url=https://example.test/json&query=$[0]')
.intercept(nock =>
- nock('https://example.test')
- .get('/json')
- .reply(200, '["foo"]')
+ nock('https://example.test').get('/json').reply(200, '["foo"]'),
)
.expectBadge({
label: 'custom badge',
@@ -173,14 +214,11 @@ t.create('JSON contains an array')
})
t.create('JSON contains a string')
- .get('.json?url=https://example.test/json&query=$.foo,')
+ .get('.json?url=https://example.test/json&query=$')
.intercept(nock =>
- nock('https://example.test')
- .get('/json')
- .reply(200, '"foo"')
+ nock('https://example.test').get('/json').reply(200, '"foo"'),
)
.expectBadge({
label: 'custom badge',
- message: 'resource must contain an object or array',
- color: 'lightgrey',
+ message: 'foo',
})
diff --git a/services/dynamic/dynamic-regex.service.js b/services/dynamic/dynamic-regex.service.js
new file mode 100644
index 0000000000000..39ae3328174a7
--- /dev/null
+++ b/services/dynamic/dynamic-regex.service.js
@@ -0,0 +1,116 @@
+import Joi from 'joi'
+import RE2 from 're2'
+import {
+ BaseService,
+ InvalidParameter,
+ InvalidResponse,
+ queryParams,
+} from '../index.js'
+import { url } from '../validators.js'
+import { httpErrors, renderDynamicBadge } from '../dynamic-common.js'
+
+const VALID_FLAGS = 'ims'
+// Note: both the 'U' flag and the '-' anti-flag don't work in this particular re2 implementation
+// Also, there are other flags that are supported, but they modify the javascript search and elements returned, which are not configurable by the user
+// For now we only allow the documented & supported ones
+
+export default class DynamicRegex extends BaseService {
+ static category = 'dynamic'
+ static route = {
+ base: `badge/dynamic/regex`,
+ pattern: '',
+ queryParamSchema: Joi.object({
+ url,
+ search: Joi.string().required(),
+ replace: Joi.string().optional(),
+ flags: Joi.string().optional(),
+ }),
+ }
+ static openApi = {
+ '/badge/dynamic/regex': {
+ get: {
+ summary: 'Dynamic Regex Badge',
+ description:
+ '⚠️ Experimental: This badge is considered experimental and may change or be removed at any time.\n\nThis badge will extract text from a file using re2 (a subset of regex: https://github.com/google/re2).\nThe main use-case is to extract values from unstructured plain-text files.\nFor example: if a file contains a line like `version - 2.4` you can extract the value `2.4` by using a search regex of `version - (.*)` and `$1` as replacement.\n\nFull Syntax documentation here: https://github.com/google/re2/wiki/Syntax',
+ parameters: queryParams(
+ {
+ name: 'url',
+ description:
+ 'The URL to a file to search. The full raw content will be used as the search string.',
+ required: true,
+ example:
+ 'https://raw.githubusercontent.com/badges/shields/refs/heads/master/README.md',
+ },
+ {
+ name: 'search',
+ description:
+ 'A re2 expression that will be used to extract data from the document. Only the first matched text will be returned.',
+ required: true,
+ example: 'Every (.\\*?) it serves (?.\\*?) images',
+ },
+ {
+ name: 'replace',
+ description:
+ 'A replacement string that will be used as the replacement of the search regex. Use `$$` to escape a `$` sign, `$n` to specify the nth matched group, `$` for a named group, etc. If empty (default), no replacement will be done and the full matched text will be shown.',
+ required: false,
+ example: '$/$1',
+ },
+ {
+ name: 'flags',
+ description:
+ 'Flags to be used when creating the regex: `i` = case-insensitive, `m` = multi-line mode, `s` = dot matches linebreaks. None by default.',
+ required: false,
+ example: VALID_FLAGS,
+ },
+ ),
+ },
+ },
+ }
+ static defaultBadgeData = { label: 'match' }
+
+ static validate(flags) {
+ if (flags?.split('')?.some(c => VALID_FLAGS.indexOf(c) === -1)) {
+ throw new InvalidParameter({
+ prettyMessage: `Invalid flags, must be one of: ${VALID_FLAGS}`,
+ })
+ }
+ }
+
+ static transform(buffer, search, replace, flags) {
+ // build re2 regex
+ let re2
+ try {
+ re2 = new RE2(search, flags)
+ } catch (error) {
+ if (error instanceof SyntaxError) {
+ throw new InvalidParameter({
+ prettyMessage: `Invalid re2 regex: ${error.message}`,
+ })
+ }
+ throw error
+ }
+
+ // extract value
+ const found = re2.exec(buffer)
+ if (found == null) {
+ throw new InvalidResponse({
+ prettyMessage: 'no result',
+ })
+ }
+
+ // replace if needed
+ return replace !== undefined ? found[0].replace(re2, replace) : found[0]
+ }
+
+ async handle(namedParams, { url, search, replace, flags }) {
+ this.constructor.validate(flags)
+ const { buffer } = await this._request({
+ url,
+ httpErrors,
+ logErrors: [],
+ options: { timeout: { request: 3500 } },
+ })
+ const value = this.constructor.transform(buffer, search, replace, flags)
+ return renderDynamicBadge({ value })
+ }
+}
diff --git a/services/dynamic/dynamic-regex.spec.js b/services/dynamic/dynamic-regex.spec.js
new file mode 100644
index 0000000000000..bc308071bf1af
--- /dev/null
+++ b/services/dynamic/dynamic-regex.spec.js
@@ -0,0 +1,97 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidParameter, InvalidResponse } from '../index.js'
+import DynamicRegex from './dynamic-regex.service.js'
+
+describe('validate function', function () {
+ it('validates flags correctly', function () {
+ expect(() => DynamicRegex.validate(undefined)).to.not.throw()
+ expect(() => DynamicRegex.validate('')).to.not.throw()
+ expect(() => DynamicRegex.validate('i')).to.not.throw()
+ expect(() => DynamicRegex.validate('m')).to.not.throw()
+ expect(() => DynamicRegex.validate('s')).to.not.throw()
+ expect(() => DynamicRegex.validate('U')).to.throw(InvalidParameter)
+ expect(() => DynamicRegex.validate('-i')).to.throw(InvalidParameter)
+ expect(() => DynamicRegex.validate('-imsU0')).to.throw(InvalidParameter)
+ })
+})
+
+describe('transform function', function () {
+ it('invalid regexes', function () {
+ expect(() =>
+ DynamicRegex.transform('', 'x(?=y)', undefined, undefined),
+ ).to.throw(InvalidParameter)
+ expect(() =>
+ DynamicRegex.transform('', '(.)\\1', undefined, undefined),
+ ).to.throw(InvalidParameter)
+ expect(() =>
+ DynamicRegex.transform('', '\\V', undefined, undefined),
+ ).to.throw(InvalidParameter)
+ expect(() =>
+ DynamicRegex.transform('', '\\U', undefined, undefined),
+ ).to.throw(InvalidParameter)
+ expect(() =>
+ DynamicRegex.transform('', '(*ACCEPT)', undefined, undefined),
+ ).to.throw(InvalidParameter)
+ })
+
+ it('no result', function () {
+ expect(() =>
+ DynamicRegex.transform('abc', 'x', undefined, undefined),
+ ).to.throw(InvalidResponse)
+ expect(() =>
+ DynamicRegex.transform('abc', 'A', undefined, undefined),
+ ).to.throw(InvalidResponse)
+ expect(() =>
+ DynamicRegex.transform('abc', '^b', undefined, undefined),
+ ).to.throw(InvalidResponse)
+ })
+
+ it('found result', function () {
+ test(DynamicRegex.transform, () => {
+ given('abc', '.*', undefined, undefined).expect('abc')
+ given('abc', '.b', undefined, undefined).expect('ab')
+ given('abc', 'c$', undefined, undefined).expect('c')
+ })
+ })
+
+ /*
+ 1. Do this outside the test function so we're not including
+ the time taken to generate the string in the timeout
+ 2. Generate ~10Mb text - the largest response we would accept
+ */
+ const redosInput = `${'a'.repeat(10_000_000)}x`
+
+ it('ReDoS', function (done) {
+ this.timeout(200 /*milliseconds*/)
+
+ // ReDoS examples taken from https://en.wikipedia.org/wiki/ReDoS
+ expect(() =>
+ DynamicRegex.transform(redosInput, '(a|a)+$', undefined, undefined),
+ ).to.throw(InvalidResponse)
+ expect(() =>
+ DynamicRegex.transform(redosInput, '(a+)*$', undefined, undefined),
+ ).to.not.throw()
+ expect(() =>
+ DynamicRegex.transform(redosInput, 'a*b?a*c', undefined, undefined),
+ ).to.throw(InvalidResponse)
+
+ done()
+ })
+
+ it('replace result', function () {
+ test(DynamicRegex.transform, () => {
+ given('abc', 'a(.)c', '$1', undefined).expect('b')
+ given('abc', '(.)(.)', '$2', undefined).expect('b')
+ given('abc', '(?.)', '$', undefined).expect('a')
+ })
+ })
+
+ it('flags usage', function () {
+ test(DynamicRegex.transform, () => {
+ given('abc', 'A', undefined, 'i').expect('a')
+ given('abc\ndef', '^d', undefined, 'm').expect('d')
+ given('abc\ndef', 'c(.)', '$1', 's').expect('\n')
+ })
+ })
+})
diff --git a/services/dynamic/dynamic-regex.tester.js b/services/dynamic/dynamic-regex.tester.js
new file mode 100644
index 0000000000000..07bb737a3f934
--- /dev/null
+++ b/services/dynamic/dynamic-regex.tester.js
@@ -0,0 +1,123 @@
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+////////// OK tests //////////
+
+t.create('Search only')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=unblocking \\[.*\\]',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'unblocking [#4947]',
+ color: 'blue',
+ })
+
+t.create('Search & replace with flags')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=Unblocking \\[(.*)\\]&replace=$1&flags=i',
+ )
+ .expectBadge({
+ label: 'match',
+ message: '#4947',
+ color: 'blue',
+ })
+
+////////// KO tests (specific) //////////
+
+t.create('No result')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=notfound',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'no result',
+ color: 'lightgrey',
+ })
+
+t.create('Invalid Regex')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=x(%3F%3Dy)',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'Invalid re2 regex: invalid perl operator: (?=',
+ color: 'red',
+ })
+
+t.create('Invalid flags')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=questions.*providing&flags=s0',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'Invalid flags, must be one of: ims',
+ color: 'red',
+ })
+
+////////// KO tests (common) //////////
+
+t.create('No URL specified').get('.json?search=.*&label=Found').expectBadge({
+ label: 'Found',
+ message: 'invalid query parameter: url',
+ color: 'red',
+})
+
+t.create('No search specified')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/README.md&label=Found',
+ )
+ .expectBadge({
+ label: 'Found',
+ message: 'invalid query parameter: search',
+ color: 'red',
+ })
+
+t.create('Invalid url')
+ .get(
+ '.json?url=https://github.com/badges/shields/raw/master/notafile.json&search=.*',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'resource not found',
+ color: 'red',
+ })
+
+t.create('Malformed url')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/%0README.md&search=.*&label=Found',
+ )
+ .expectBadge({
+ label: 'Found',
+ message: 'invalid',
+ color: 'lightgrey',
+ })
+
+t.create('User color overrides default')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/badges/shields/refs/heads/master/frontend/blog/2024-07-10-sunsetting-shields-custom-logos.md&search=unblocking \\[.*\\]&color=10ADED',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'unblocking [#4947]',
+ color: '#10aded',
+ })
+
+t.create('Error color overrides default')
+ .get(
+ '.json?url=https://github.com/badges/shields/raw/master/notafile.json&search=$1',
+ )
+ .expectBadge({
+ label: 'match',
+ message: 'resource not found',
+ color: 'red',
+ })
+
+t.create('Error color overrides user specified')
+ .get('.json?search=$1&color=10ADED')
+ .expectBadge({
+ label: 'match',
+ message: 'invalid query parameter: url',
+ color: 'red',
+ })
diff --git a/services/dynamic/dynamic-response-fixtures.js b/services/dynamic/dynamic-response-fixtures.js
index 25af9ff15ad56..774483608e89b 100644
--- a/services/dynamic/dynamic-response-fixtures.js
+++ b/services/dynamic/dynamic-response-fixtures.js
@@ -1,7 +1,4 @@
-'use strict'
-
-module.exports = {
- exampleXml: `
+export const exampleXml = `
Gambardella, Matthew
@@ -121,5 +118,4 @@ module.exports = {
environment.
-`,
-}
+`
diff --git a/services/dynamic/dynamic-toml.service.js b/services/dynamic/dynamic-toml.service.js
new file mode 100644
index 0000000000000..d4527d1b50380
--- /dev/null
+++ b/services/dynamic/dynamic-toml.service.js
@@ -0,0 +1,58 @@
+import { MetricNames } from '../../core/base-service/metric-helper.js'
+import { BaseTomlService, queryParams } from '../index.js'
+import { createRoute } from './dynamic-helpers.js'
+import jsonPath from './json-path.js'
+
+const description = `
+The Dynamic TOML Badge allows you to extract an arbitrary value from any
+TOML Document using a JSONPath selector and show it on a badge.
+`
+
+export default class DynamicToml extends jsonPath(BaseTomlService) {
+ static enabledMetrics = [MetricNames.SERVICE_RESPONSE_SIZE]
+ static route = createRoute('toml')
+ static openApi = {
+ '/badge/dynamic/toml': {
+ get: {
+ summary: 'Dynamic TOML Badge',
+ description,
+ parameters: queryParams(
+ {
+ name: 'url',
+ description: 'The URL to a TOML document',
+ required: true,
+ example:
+ 'https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml',
+ },
+ {
+ name: 'query',
+ description:
+ 'A JSONPath expression that will be used to query the document',
+ required: true,
+ example: '$.title',
+ },
+ {
+ name: 'prefix',
+ description: 'Optional prefix to append to the value',
+ example: '[',
+ },
+ {
+ name: 'suffix',
+ description: 'Optional suffix to append to the value',
+ example: ']',
+ },
+ ),
+ },
+ },
+ }
+
+ async fetch({ schema, url, httpErrors }) {
+ return this._requestToml({
+ schema,
+ url,
+ httpErrors,
+ logErrors: [],
+ options: { timeout: { request: 3500 } },
+ })
+ }
+}
diff --git a/services/dynamic/dynamic-toml.tester.js b/services/dynamic/dynamic-toml.tester.js
new file mode 100644
index 0000000000000..edbf056cda7b9
--- /dev/null
+++ b/services/dynamic/dynamic-toml.tester.js
@@ -0,0 +1,107 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('No URL specified')
+ .get('.json?query=$.name&label=Package Name')
+ .expectBadge({
+ label: 'Package Name',
+ message: 'invalid query parameter: url',
+ color: 'red',
+ })
+
+t.create('No query specified')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&label=Package Name',
+ )
+ .expectBadge({
+ label: 'Package Name',
+ message: 'invalid query parameter: query',
+ color: 'red',
+ })
+
+t.create('TOML from url')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.title',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'TOML Example',
+ color: 'blue',
+ })
+
+t.create('TOML from url | multiple results')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.database.data[0][*]',
+ )
+ .expectBadge({ label: 'custom badge', message: 'delta, phi' })
+
+t.create('TOML from url | caching with new query params')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.owner.name',
+ )
+ .expectBadge({ label: 'custom badge', message: 'Tom Preston-Werner' })
+
+t.create('TOML from url | with prefix & suffix & label')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.database.temp_targets.cpu&prefix=%2B&suffix=%C2%B0C&label=CPU Temp Target',
+ )
+ .expectBadge({ label: 'CPU Temp Target', message: '+79.5°C' })
+
+t.create('TOML from url | object doesnt exist')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.does_not_exist',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'no result',
+ color: 'lightgrey',
+ })
+
+t.create('TOML from url | invalid url')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/not-a-file.toml&query=$.version',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'resource not found',
+ color: 'red',
+ })
+
+t.create('TOML from url | user color overrides default')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.title&color=10ADED',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'TOML Example',
+ color: '#10aded',
+ })
+
+t.create('TOML from url | error color overrides default')
+ .get(
+ '.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/not-a-file.toml&query=$.version',
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'resource not found',
+ color: 'red',
+ })
+
+t.create('TOML from url | error color overrides user specified')
+ .get('.json?query=$.version&color=10ADED')
+ .expectBadge({
+ label: 'custom badge',
+ message: 'invalid query parameter: url',
+ color: 'red',
+ })
+
+t.create('TOML contains a string')
+ .get('.json?url=https://example.test/toml&query=$.foo,')
+ .intercept(nock =>
+ nock('https://example.test').get('/toml').reply(200, '"foo"'),
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'unparseable toml response',
+ color: 'lightgrey',
+ })
diff --git a/services/dynamic/dynamic-xml.service.js b/services/dynamic/dynamic-xml.service.js
index 2867f1566c348..effa2ac0f6ffe 100644
--- a/services/dynamic/dynamic-xml.service.js
+++ b/services/dynamic/dynamic-xml.service.js
@@ -1,11 +1,30 @@
-'use strict'
+import { DOMParser, MIME_TYPE } from '@xmldom/xmldom'
+import xpath from 'xpath'
+import { MetricNames } from '../../core/base-service/metric-helper.js'
+import { renderDynamicBadge, httpErrors } from '../dynamic-common.js'
+import {
+ BaseService,
+ InvalidResponse,
+ InvalidParameter,
+ queryParams,
+} from '../index.js'
+import { createRoute } from './dynamic-helpers.js'
-const { DOMParser } = require('xmldom')
-const xpath = require('xpath')
-const { MetricNames } = require('../../core/base-service/metric-helper')
-const { renderDynamicBadge, errorMessages } = require('../dynamic-common')
-const { BaseService, InvalidResponse, InvalidParameter } = require('..')
-const { createRoute } = require('./dynamic-helpers')
+const MIME_TYPES = Object.values(MIME_TYPE)
+
+const description = `
+The Dynamic XML Badge allows you to extract an arbitrary value from any
+XML Document using an XPath selector and show it on a badge.
+
+Useful resources for constructing XPath selectors:
+- [XPather](http://xpather.com/)
+- [XPath Cheat Sheet](https://devhints.io/xpath/)
+
+Note: For XML documents that use a default namespace prefix, you will need to use the
+[local-name](https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/local-name)
+function to construct your query.
+For example \`/*[local-name()='myelement']\` rather than \`/myelement\`.
+`
// This service extends BaseService because it uses a different XML parser
// than BaseXmlService which can be used with xpath.
@@ -13,35 +32,72 @@ const { createRoute } = require('./dynamic-helpers')
// One way to create a more performant version would be to use the BaseXml
// JSON parser and write the queries in jsonpath instead. Then eventually
// deprecate the old version.
-module.exports = class DynamicXml extends BaseService {
- static get category() {
- return 'dynamic'
+export default class DynamicXml extends BaseService {
+ static category = 'dynamic'
+ static enabledMetrics = [MetricNames.SERVICE_RESPONSE_SIZE]
+ static route = createRoute('xml')
+ static openApi = {
+ '/badge/dynamic/xml': {
+ get: {
+ summary: 'Dynamic XML Badge',
+ description,
+ parameters: queryParams(
+ {
+ name: 'url',
+ description: 'The URL to a XML document',
+ required: true,
+ example: 'https://httpbin.org/xml',
+ },
+ {
+ name: 'query',
+ description:
+ 'An XPath expression that will be used to query the document',
+ required: true,
+ example: '//slideshow/slide[1]/title',
+ },
+ {
+ name: 'prefix',
+ description: 'Optional prefix to append to the value',
+ example: '[',
+ },
+ {
+ name: 'suffix',
+ description: 'Optional suffix to append to the value',
+ example: ']',
+ },
+ ),
+ },
+ },
}
- static get enabledMetrics() {
- return [MetricNames.SERVICE_RESPONSE_SIZE]
- }
+ static defaultBadgeData = { label: 'custom badge' }
- static get route() {
- return createRoute('xml')
- }
-
- static get defaultBadgeData() {
- return {
- label: 'custom badge',
- }
+ getmimeType(contentType) {
+ return MIME_TYPES.find(mime => contentType.includes(mime)) ?? 'text/xml'
}
- transform({ pathExpression, buffer }) {
+ transform({ pathExpression, buffer, contentType = 'text/xml' }) {
// e.g. //book[2]/@id
const pathIsAttr = (
pathExpression.split('/').slice(-1)[0] || ''
).startsWith('@')
- const parsed = new DOMParser().parseFromString(buffer)
+
+ let parsed
+ try {
+ parsed = new DOMParser().parseFromString(buffer, contentType)
+ } catch (e) {
+ throw new InvalidResponse({ prettyMessage: e.message })
+ }
let values
try {
- values = xpath.select(pathExpression, parsed)
+ if (contentType === 'text/html') {
+ values = xpath
+ .parse(pathExpression)
+ .select({ node: parsed, isHtml: true })
+ } else {
+ values = xpath.select(pathExpression, parsed)
+ }
} catch (e) {
throw new InvalidParameter({ prettyMessage: e.message })
}
@@ -78,15 +134,25 @@ module.exports = class DynamicXml extends BaseService {
}
async handle(_namedParams, { url, query: pathExpression, prefix, suffix }) {
- const { buffer } = await this._request({
+ const { buffer, res } = await this._request({
url,
- options: { headers: { Accept: 'application/xml, text/xml' } },
- errorMessages,
+ options: {
+ headers: { Accept: 'application/xml, text/xml' },
+ timeout: { request: 3500 },
+ },
+ httpErrors,
+ logErrors: [],
})
+ let contentType = 'text/xml'
+ if (res.headers['content-type']) {
+ contentType = this.getmimeType(res.headers['content-type'])
+ }
+
const { values: value } = this.transform({
pathExpression,
buffer,
+ contentType,
})
return renderDynamicBadge({ value, prefix, suffix })
diff --git a/services/dynamic/dynamic-xml.spec.js b/services/dynamic/dynamic-xml.spec.js
index ce5d209baf733..c3f1b64c91ab3 100644
--- a/services/dynamic/dynamic-xml.spec.js
+++ b/services/dynamic/dynamic-xml.spec.js
@@ -1,11 +1,9 @@
-'use strict'
-
-const { expect } = require('chai')
-const sinon = require('sinon')
-const xpath = require('xpath')
-const { test, given } = require('sazerac')
-const { InvalidResponse } = require('..')
-const DynamicXml = require('./dynamic-xml.service')
+import { expect } from 'chai'
+import sinon from 'sinon'
+import xpath from 'xpath'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import DynamicXml from './dynamic-xml.service.js'
const exampleXml = `
@@ -22,22 +20,45 @@ const exampleXml = `
`
-describe('DynamicXml', function() {
- describe('transform()', function() {
- beforeEach(function() {
+const exampleHtml = `
+
+
+
+
+ Herman Melville - Moby-Dick
+
+
+ Availing himself of the mild, summer-cool weather that now reigned in these
+ latitudes, and in preparation for the peculiarly active pursuits shortly to
+ be anticipated, Perth, the begrimed, blistered old blacksmith, had not
+ removed his portable forge to the hold again, after concluding his
+ contributory work for Ahab's leg, but still retained it on deck, fast lashed
+ to ringbolts by the foremast; being now almost incessantly invoked by the
+ headsmen, and harpooneers, and bowsmen to do some little job for them;
+ altering, or repairing, or new shaping their various weapons and boat
+ furniture.
+
+
+
+
+`
+
+describe('DynamicXml', function () {
+ describe('transform()', function () {
+ beforeEach(function () {
sinon.stub(xpath, 'select').returns(undefined)
})
- afterEach(function() {
+ afterEach(function () {
sinon.restore()
})
- it('throws InvalidResponse on unsupported query', function() {
+ it('throws InvalidResponse on unsupported query', function () {
expect(() =>
DynamicXml.prototype.transform({
pathExpression: '//book/title',
buffer: exampleXml,
- })
+ }),
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'unsupported query')
@@ -54,7 +75,7 @@ describe('DynamicXml', function() {
given({ pathExpression: '//book/title/text()', buffer: exampleXml }).expect(
{
values: ["XML Developer's Guide", 'Midnight Rain'],
- }
+ },
)
given({
pathExpression: 'string(//book[1]/title)',
@@ -128,5 +149,37 @@ describe('DynamicXml', function() {
}).expect({
values: ["XML Developer's Guide", '44.95'],
})
+ given({
+ pathExpression: '//h1[1]',
+ buffer: exampleHtml,
+ contentType: 'text/html',
+ }).expect({
+ values: ['Herman Melville - Moby-Dick'],
+ })
+
+ // lowercase doctype
+ // https://github.com/badges/shields/issues/10827
+ given({
+ pathExpression: '//h1[1]',
+ buffer: exampleHtml.replace('', ''),
+ contentType: 'text/html',
+ }).expect({
+ values: ['Herman Melville - Moby-Dick'],
+ })
+ })
+
+ test(DynamicXml.prototype.getmimeType, () => {
+ // known types
+ given('text/html').expect('text/html')
+ given('application/xml').expect('application/xml')
+ given('application/xhtml+xml').expect('application/xhtml+xml')
+ given('image/svg+xml').expect('image/svg+xml')
+
+ // with character set
+ given('text/html; charset=utf-8').expect('text/html')
+
+ // should fall back to text/xml if mime type is not one of the known types
+ given('text/csv').expect('text/xml')
+ given('foobar').expect('text/xml')
})
})
diff --git a/services/dynamic/dynamic-xml.tester.js b/services/dynamic/dynamic-xml.tester.js
index adca35606b4a6..e84b834ac83e4 100644
--- a/services/dynamic/dynamic-xml.tester.js
+++ b/services/dynamic/dynamic-xml.tester.js
@@ -1,14 +1,11 @@
-'use strict'
-
-const queryString = require('query-string')
-const t = (module.exports = require('../tester').createServiceTester())
-const { exampleXml } = require('./dynamic-response-fixtures')
+import qs from 'qs'
+import { createServiceTester } from '../tester.js'
+import { exampleXml } from './dynamic-response-fixtures.js'
+export const t = await createServiceTester()
const exampleUrl = 'https://example.test/example.xml'
const withExampleXml = nock =>
- nock('https://example.test')
- .get('/example.xml')
- .reply(200, exampleXml)
+ nock('https://example.test').get('/example.xml').reply(200, exampleXml)
t.create('No URL specified')
.get('.json?query=//name&label=Package Name')
@@ -28,10 +25,10 @@ t.create('No query specified')
t.create('XML from url')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: "//book[@id='bk102']/title",
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -42,10 +39,10 @@ t.create('XML from url')
t.create('uri query parameter alias')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
uri: exampleUrl,
query: "//book[@id='bk102']/title",
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -56,10 +53,10 @@ t.create('uri query parameter alias')
t.create('attribute')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//book[2]/@id',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -69,26 +66,27 @@ t.create('attribute')
t.create('multiple results')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//book/title',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
label: 'custom badge',
+ // truncated to 255 chars
message:
- "XML Developer's Guide, Midnight Rain, Maeve Ascendant, Oberon's Legacy, The Sundered Grail, Lover Birds, Splish Splash, Creepy Crawlies, Paradox Lost, Microsoft .NET: The Programming Bible, MSXML3: A Comprehensive Guide, Visual Studio 7: A Comprehensive Guide",
+ "XML Developer's Guide, Midnight Rain, Maeve Ascendant, Oberon's Legacy, The Sundered Grail, Lover Birds, Splish Splash, Creepy Crawlies, Paradox Lost, Microsoft .NET: The Programming Bible, MSXML3: A Comprehensive Guide, Visual Studio 7: A Comprehensive G",
})
t.create('prefix and suffix')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: "//book[@id='bk102']/title",
prefix: 'title is ',
suffix: ', innit',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -97,10 +95,10 @@ t.create('prefix and suffix')
t.create('query doesnt exist')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//does/not/exist',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -111,10 +109,10 @@ t.create('query doesnt exist')
t.create('query doesnt exist (attribute)')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//does/not/@exist',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -125,10 +123,10 @@ t.create('query doesnt exist (attribute)')
t.create('Cannot resolve QName')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//a:si',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -139,10 +137,10 @@ t.create('Cannot resolve QName')
t.create('XPath parse error')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//a[contains(@href, "foo"]',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -153,7 +151,7 @@ t.create('XPath parse error')
t.create('XML from url | invalid url')
.get(
- '.json?url=https://github.com/badges/shields/raw/master/notafile.xml&query=//version'
+ '.json?url=https://github.com/badges/shields/raw/master/notafile.xml&query=//version',
)
.expectBadge({
label: 'custom badge',
@@ -163,26 +161,26 @@ t.create('XML from url | invalid url')
t.create('request should set Accept header')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: "//book[@id='bk102']/title",
- })}`
+ })}`,
)
.intercept(nock =>
nock('https://example.test', {
reqheaders: { accept: 'application/xml, text/xml' },
})
.get('/example.xml')
- .reply(200, exampleXml)
+ .reply(200, exampleXml),
)
.expectBadge({ label: 'custom badge', message: 'Midnight Rain' })
t.create('query with node function')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: '//book[1]/title/text()',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -191,12 +189,12 @@ t.create('query with node function')
color: 'blue',
})
-t.create('query with type convertion to string')
+t.create('query with type conversion to string')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: 'string(//book[1]/title)',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -205,12 +203,12 @@ t.create('query with type convertion to string')
color: 'blue',
})
-t.create('query with type convertion to number')
+t.create('query with type conversion to number')
.get(
- `.json?${queryString.stringify({
+ `.json?${qs.stringify({
url: exampleUrl,
query: 'number(//book[1]/price)',
- })}`
+ })}`,
)
.intercept(withExampleXml)
.expectBadge({
@@ -218,3 +216,16 @@ t.create('query with type convertion to number')
message: '44.95',
color: 'blue',
})
+
+t.create('query HTML document')
+ .get(
+ `.json?${qs.stringify({
+ url: 'https://httpbin.org/html',
+ query: '//h1[1]',
+ })}`,
+ )
+ .expectBadge({
+ label: 'custom badge',
+ message: 'Herman Melville - Moby-Dick',
+ color: 'blue',
+ })
diff --git a/services/dynamic/dynamic-yaml.service.js b/services/dynamic/dynamic-yaml.service.js
index 9e96ada0a2a1d..5fd7e80da340d 100644
--- a/services/dynamic/dynamic-yaml.service.js
+++ b/services/dynamic/dynamic-yaml.service.js
@@ -1,24 +1,58 @@
-'use strict'
+import { MetricNames } from '../../core/base-service/metric-helper.js'
+import { BaseYamlService, queryParams } from '../index.js'
+import { createRoute } from './dynamic-helpers.js'
+import jsonPath from './json-path.js'
-const { MetricNames } = require('../../core/base-service/metric-helper')
-const { BaseYamlService } = require('..')
-const { createRoute } = require('./dynamic-helpers')
-const jsonPath = require('./json-path')
+const description = `
+The Dynamic YAML Badge allows you to extract an arbitrary value from any
+YAML Document using a JSONPath selector and show it on a badge.
+`
-module.exports = class DynamicYaml extends jsonPath(BaseYamlService) {
- static get enabledMetrics() {
- return [MetricNames.SERVICE_RESPONSE_SIZE]
+export default class DynamicYaml extends jsonPath(BaseYamlService) {
+ static enabledMetrics = [MetricNames.SERVICE_RESPONSE_SIZE]
+ static route = createRoute('yaml')
+ static openApi = {
+ '/badge/dynamic/yaml': {
+ get: {
+ summary: 'Dynamic YAML Badge',
+ description,
+ parameters: queryParams(
+ {
+ name: 'url',
+ description: 'The URL to a YAML document',
+ required: true,
+ example:
+ 'https://raw.githubusercontent.com/badges/shields/master/.github/dependabot.yml',
+ },
+ {
+ name: 'query',
+ description:
+ 'A JSONPath expression that will be used to query the document',
+ required: true,
+ example: '$.version',
+ },
+ {
+ name: 'prefix',
+ description: 'Optional prefix to append to the value',
+ example: '[',
+ },
+ {
+ name: 'suffix',
+ description: 'Optional suffix to append to the value',
+ example: ']',
+ },
+ ),
+ },
+ },
}
- static get route() {
- return createRoute('yaml')
- }
-
- async fetch({ schema, url, errorMessages }) {
+ async fetch({ schema, url, httpErrors }) {
return this._requestYaml({
schema,
url,
- errorMessages,
+ httpErrors,
+ logErrors: [],
+ options: { timeout: { request: 3500 } },
})
}
}
diff --git a/services/dynamic/dynamic-yaml.tester.js b/services/dynamic/dynamic-yaml.tester.js
index cfcc4f3dd293d..5a93004a42ee0 100644
--- a/services/dynamic/dynamic-yaml.tester.js
+++ b/services/dynamic/dynamic-yaml.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('No URL specified')
.get('.json?query=$.name&label=Package Name')
@@ -12,7 +11,7 @@ t.create('No URL specified')
t.create('No query specified')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&label=Package Name'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&label=Package Name',
)
.expectBadge({
label: 'Package Name',
@@ -22,7 +21,7 @@ t.create('No query specified')
t.create('YAML from url')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name',
)
.expectBadge({
label: 'custom badge',
@@ -32,7 +31,7 @@ t.create('YAML from url')
t.create('YAML from uri (support uri query parameter)')
.get(
- '.json?uri=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name'
+ '.json?uri=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name',
)
.expectBadge({
label: 'custom badge',
@@ -42,25 +41,25 @@ t.create('YAML from uri (support uri query parameter)')
t.create('YAML from url | multiple results')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$..keywords[0:2:1]'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$..keywords[0:2:1]',
)
.expectBadge({ label: 'custom badge', message: 'coredns, dns' })
t.create('YAML from url | caching with new query params')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version',
)
.expectBadge({ label: 'custom badge', message: '0.8.0' })
t.create('YAML from url | with prefix & suffix & label')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version&prefix=v&suffix= dev&label=Shields'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version&prefix=v&suffix= dev&label=Shields',
)
.expectBadge({ label: 'Shields', message: 'v0.8.0 dev' })
t.create('YAML from url | object doesnt exist')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.does_not_exist'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.does_not_exist',
)
.expectBadge({
label: 'custom badge',
@@ -70,7 +69,7 @@ t.create('YAML from url | object doesnt exist')
t.create('YAML from url | invalid url')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version',
)
.expectBadge({
label: 'custom badge',
@@ -80,13 +79,13 @@ t.create('YAML from url | invalid url')
t.create('YAML from url | user color overrides default')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&color=10ADED'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&color=10ADED',
)
.expectBadge({ label: 'custom badge', message: 'coredns', color: '#10aded' })
t.create('YAML from url | error color overrides default')
.get(
- '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version'
+ '.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version',
)
.expectBadge({
label: 'custom badge',
@@ -103,14 +102,11 @@ t.create('YAML from url | error color overrides user specified')
})
t.create('YAML contains a string')
- .get('.json?url=https://example.test/yaml&query=$.foo,')
+ .get('.json?url=https://example.test/yaml&query=$,')
.intercept(nock =>
- nock('https://example.test')
- .get('/yaml')
- .reply(200, '"foo"')
+ nock('https://example.test').get('/yaml').reply(200, '"foo"'),
)
.expectBadge({
label: 'custom badge',
- message: 'resource must contain an object or array',
- color: 'lightgrey',
+ message: 'foo',
})
diff --git a/services/dynamic/json-path.js b/services/dynamic/json-path.js
index 00537dbfe990b..48ebf05d9374c 100644
--- a/services/dynamic/json-path.js
+++ b/services/dynamic/json-path.js
@@ -2,12 +2,10 @@
* @module
*/
-'use strict'
-
-const Joi = require('@hapi/joi')
-const jp = require('jsonpath')
-const { renderDynamicBadge, errorMessages } = require('../dynamic-common')
-const { InvalidParameter, InvalidResponse } = require('..')
+import Joi from 'joi'
+import { JSONPath as jp } from 'jsonpath-plus'
+import { renderDynamicBadge, httpErrors } from '../dynamic-common.js'
+import { InvalidParameter, InvalidResponse } from '../index.js'
/**
* Dynamic service class factory which wraps {@link module:core/base-service/base~BaseService} with support of {@link https://jsonpath.com/|JSONPath}.
@@ -15,17 +13,10 @@ const { InvalidParameter, InvalidResponse } = require('..')
* @param {Function} superclass class to extend
* @returns {Function} wrapped class
*/
-module.exports = superclass =>
+export default superclass =>
class extends superclass {
- static get category() {
- return 'dynamic'
- }
-
- static get defaultBadgeData() {
- return {
- label: 'custom badge',
- }
- }
+ static category = 'dynamic'
+ static defaultBadgeData = { label: 'custom badge' }
/**
* Request data from an upstream API, transform it to JSON and validate against a schema
@@ -33,15 +24,15 @@ module.exports = superclass =>
* @param {object} attrs Refer to individual attrs
* @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
* @param {string} attrs.url URL to request
- * @param {object} [attrs.errorMessages={}] Key-value map of status codes
+ * @param {object} [attrs.httpErrors={}] Key-value map of status codes
* and custom error messages e.g: `{ 404: 'package not found' }`.
* This can be used to extend or override the
* [default](https://github.com/badges/shields/blob/master/services/dynamic-common.js#L8)
* @returns {object} Parsed response
*/
- async fetch({ schema, url, errorMessages }) {
+ async fetch({ schema, url, httpErrors }) {
throw new Error(
- `fetch() function not implemented for ${this.constructor.name}`
+ `fetch() function not implemented for ${this.constructor.name}`,
)
}
@@ -49,35 +40,27 @@ module.exports = superclass =>
const data = await this.fetch({
schema: Joi.any(),
url,
- errorMessages,
+ httpErrors,
})
- // JSONPath only works on objects and arrays.
- // https://github.com/badges/shields/issues/4018
- if (typeof data !== 'object') {
- throw new InvalidResponse({
- prettyMessage: 'resource must contain an object or array',
- })
- }
-
let values
try {
- values = jp.query(data, pathExpression)
+ values = jp({ json: data, path: pathExpression, eval: false })
} catch (e) {
const { message } = e
if (
- message.startsWith('Lexical error') ||
- message.startsWith('Parse error')
+ message.includes('prevented in JSONPath expression') ||
+ e instanceof TypeError
) {
throw new InvalidParameter({
- prettyMessage: 'unparseable jsonpath query',
+ prettyMessage: 'query not supported',
})
} else {
throw e
}
}
- if (!values.length) {
+ if (!values || !values.length) {
throw new InvalidResponse({ prettyMessage: 'no result' })
}
diff --git a/services/dynamic/json-path.spec.js b/services/dynamic/json-path.spec.js
index decaf3877964a..b67e4c5b061a4 100644
--- a/services/dynamic/json-path.spec.js
+++ b/services/dynamic/json-path.spec.js
@@ -1,21 +1,18 @@
-'use strict'
+import { expect, use } from 'chai'
+import chaiAsPromised from 'chai-as-promised'
+import jsonPath from './json-path.js'
+use(chaiAsPromised)
-const chai = require('chai')
-const { expect } = chai
-const jsonPath = require('./json-path')
-
-chai.use(require('chai-as-promised'))
-
-describe('JSON Path service factory', function() {
- describe('fetch()', function() {
- it('should throw error if it is not overridden', function() {
+describe('JSON Path service factory', function () {
+ describe('fetch()', function () {
+ it('should throw error if it is not overridden', function () {
class BaseService {}
class JsonPathService extends jsonPath(BaseService) {}
const jsonPathServiceInstance = new JsonPathService()
return expect(jsonPathServiceInstance.fetch({})).to.be.rejectedWith(
Error,
- 'fetch() function not implemented for JsonPathService'
+ 'fetch() function not implemented for JsonPathService',
)
})
})
diff --git a/services/eclipse-marketplace/eclipse-marketplace-base.js b/services/eclipse-marketplace/eclipse-marketplace-base.js
index fd8c5387b04f0..558807368e659 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-base.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-base.js
@@ -1,20 +1,11 @@
-'use strict'
-
-const { BaseXmlService } = require('..')
-
-module.exports = class EclipseMarketplaceBase extends BaseXmlService {
- static buildRoute(base) {
- return {
- base,
- pattern: ':name',
- }
- }
+import { BaseXmlService } from '../index.js'
+export default class EclipseMarketplaceBase extends BaseXmlService {
async fetch({ name, schema }) {
return this._requestXml({
schema,
url: `https://marketplace.eclipse.org/content/${name}/api/p`,
- errorMessages: { 404: 'solution not found' },
+ httpErrors: { 404: 'solution not found' },
})
}
}
diff --git a/services/eclipse-marketplace/eclipse-marketplace-downloads.service.js b/services/eclipse-marketplace/eclipse-marketplace-downloads.service.js
index abd347f20c9f0..77b208b339893 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-downloads.service.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-downloads.service.js
@@ -1,81 +1,59 @@
-'use strict'
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const EclipseMarketplaceBase = require('./eclipse-marketplace-base')
-
-const monthlyResponseSchema = Joi.object({
+const downloadsResponseSchema = Joi.object({
marketplace: Joi.object({
node: Joi.object({
installsrecent: nonNegativeInteger,
- }),
- }),
-}).required()
-
-const totalResponseSchema = Joi.object({
- marketplace: Joi.object({
- node: Joi.object({
installstotal: nonNegativeInteger,
}),
}),
}).required()
-function DownloadsForInterval(interval) {
- const { base, schema, messageSuffix = '', name } = {
- month: {
- base: 'eclipse-marketplace/dm',
- messageSuffix: '/month',
- schema: monthlyResponseSchema,
- name: 'EclipseMarketplaceDownloadsMonth',
- },
- total: {
- base: 'eclipse-marketplace/dt',
- schema: totalResponseSchema,
- name: 'EclipseMarketplaceDownloadsTotal',
- },
- }[interval]
-
- return class EclipseMarketplaceDownloads extends EclipseMarketplaceBase {
- static get name() {
- return name
- }
-
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return this.buildRoute(base)
- }
+export default class EclipseMarketplaceDownloads extends EclipseMarketplaceBase {
+ static category = 'downloads'
+ static route = {
+ base: 'eclipse-marketplace',
+ pattern: ':interval(dm|dt)/:name',
+ }
- static get examples() {
- return [
- {
- title: 'Eclipse Marketplace',
- namedParams: { name: 'notepad4e' },
- staticPreview: this.render({ downloads: 30000 }),
- },
- ]
- }
+ static openApi = {
+ '/eclipse-marketplace/{interval}/{name}': {
+ get: {
+ summary: 'Eclipse Marketplace Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dt',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Monthly or Total downloads',
+ },
+ {
+ name: 'name',
+ example: 'planet-themes',
+ },
+ ),
+ },
+ },
+ }
- static render({ downloads }) {
- return {
- message: `${metric(downloads)}${messageSuffix}`,
- color: downloadCountColor(downloads),
- }
- }
+ static render({ interval, downloads }) {
+ const intervalString = interval === 'dm' ? 'month' : null
+ return renderDownloadsBadge({ downloads, interval: intervalString })
+ }
- async handle({ name }) {
- const { marketplace } = await this.fetch({ name, schema })
- const downloads =
- interval === 'total'
- ? marketplace.node.installstotal
- : marketplace.node.installsrecent
- return this.constructor.render({ downloads })
- }
+ async handle({ interval, name }) {
+ const { marketplace } = await this.fetch({
+ schema: downloadsResponseSchema,
+ name,
+ })
+ const downloads =
+ interval === 'dt'
+ ? marketplace.node.installstotal
+ : marketplace.node.installsrecent
+ return this.constructor.render({ downloads, interval })
}
}
-
-module.exports = ['month', 'total'].map(DownloadsForInterval)
diff --git a/services/eclipse-marketplace/eclipse-marketplace-downloads.tester.js b/services/eclipse-marketplace/eclipse-marketplace-downloads.tester.js
index 42a4fd17716d3..a73862ee73271 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-downloads.tester.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-downloads.tester.js
@@ -1,20 +1,16 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'EclipseMarketplaceDownloads',
title: 'EclipseMarketplaceDownloads',
pathPrefix: '/eclipse-marketplace',
-}))
+})
-t.create('total marketplace downloads')
- .get('/dt/notepad4e.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('total marketplace downloads').get('/dt/notepad4e.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('monthly marketplace downloads')
.get('/dm/notepad4e.json')
diff --git a/services/eclipse-marketplace/eclipse-marketplace-favorites.service.js b/services/eclipse-marketplace/eclipse-marketplace-favorites.service.js
index 7406cf51fb637..b13368d71e7ec 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-favorites.service.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-favorites.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const EclipseMarketplaceBase = require('./eclipse-marketplace-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
const favoritesResponseSchema = Joi.object({
marketplace: Joi.object({
@@ -12,28 +11,26 @@ const favoritesResponseSchema = Joi.object({
}),
}).required()
-module.exports = class EclipseMarketplaceFavorites extends EclipseMarketplaceBase {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return this.buildRoute('eclipse-marketplace/favorites')
+export default class EclipseMarketplaceFavorites extends EclipseMarketplaceBase {
+ static category = 'other'
+ static route = {
+ base: 'eclipse-marketplace/favorites',
+ pattern: ':name',
}
- static get examples() {
- return [
- {
- title: 'Eclipse Marketplace',
- namedParams: { name: 'notepad4e' },
- staticPreview: this.render({ favorited: 55 }),
+ static openApi = {
+ '/eclipse-marketplace/favorites/{name}': {
+ get: {
+ summary: 'Eclipse Marketplace Favorites',
+ parameters: pathParams({
+ name: 'name',
+ example: 'notepad4e',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'favorites' }
- }
+ static defaultBadgeData = { label: 'favorites' }
static render({ favorited }) {
return {
diff --git a/services/eclipse-marketplace/eclipse-marketplace-favorites.tester.js b/services/eclipse-marketplace/eclipse-marketplace-favorites.tester.js
index e8c2f77b5a3f1..74ceb06d6ab78 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-favorites.tester.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-favorites.tester.js
@@ -1,16 +1,11 @@
-'use strict'
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('favorites count')
- .get('/notepad4e.json')
- .expectBadge({
- label: 'favorites',
- message: Joi.number()
- .integer()
- .positive(),
- })
+t.create('favorites count').get('/notepad4e.json').expectBadge({
+ label: 'favorites',
+ message: Joi.number().integer().positive(),
+})
t.create('favorites for unknown solution')
.get('/this-does-not-exist.json')
diff --git a/services/eclipse-marketplace/eclipse-marketplace-license.service.js b/services/eclipse-marketplace/eclipse-marketplace-license.service.js
index 856e6d24d3f3b..bda1410e06dff 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-license.service.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-license.service.js
@@ -1,40 +1,35 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const EclipseMarketplaceBase = require('./eclipse-marketplace-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
const licenseResponseSchema = Joi.object({
marketplace: Joi.object({
node: Joi.object({
- license: Joi.string()
- .allow('')
- .required(),
+ license: Joi.string().allow('').required(),
}),
}),
}).required()
-module.exports = class EclipseMarketplaceLicense extends EclipseMarketplaceBase {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return this.buildRoute('eclipse-marketplace/l')
+export default class EclipseMarketplaceLicense extends EclipseMarketplaceBase {
+ static category = 'license'
+ static route = {
+ base: 'eclipse-marketplace/l',
+ pattern: ':name',
}
- static get examples() {
- return [
- {
- title: 'Eclipse Marketplace',
- namedParams: { name: 'notepad4e' },
- staticPreview: this.render({ license: 'GPL' }),
+ static openApi = {
+ '/eclipse-marketplace/l/{name}': {
+ get: {
+ summary: 'Eclipse Marketplace License',
+ parameters: pathParams({
+ name: 'name',
+ example: 'notepad4e',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ license }) {
if (license === '') {
diff --git a/services/eclipse-marketplace/eclipse-marketplace-license.tester.js b/services/eclipse-marketplace/eclipse-marketplace-license.tester.js
index 1c3e6aa2ef697..d12cbe61817f1 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-license.tester.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-license.tester.js
@@ -1,13 +1,10 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('license')
- .get('/notepad4e.json')
- .expectBadge({
- label: 'license',
- message: 'EPL 2.0',
- })
+t.create('license').get('/notepad4e.json').expectBadge({
+ label: 'license',
+ message: 'EPL 2.0',
+})
t.create('unspecified license')
.get('/notepad4e.json')
@@ -20,8 +17,8 @@ t.create('unspecified license')
- `
- )
+ `,
+ ),
)
.expectBadge({
label: 'license',
diff --git a/services/eclipse-marketplace/eclipse-marketplace-update.service.js b/services/eclipse-marketplace/eclipse-marketplace-update.service.js
index 2fa6e325539e8..ab16e77086d3d 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-update.service.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-update.service.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { formatDate } = require('../text-formatters')
-const { age: ageColor } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const EclipseMarketplaceBase = require('./eclipse-marketplace-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import { nonNegativeInteger } from '../validators.js'
+import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
const updateResponseSchema = Joi.object({
marketplace: Joi.object({
@@ -14,35 +12,26 @@ const updateResponseSchema = Joi.object({
}),
}).required()
-module.exports = class EclipseMarketplaceUpdate extends EclipseMarketplaceBase {
- static get category() {
- return 'activity'
- }
-
- static get route() {
- return this.buildRoute('eclipse-marketplace/last-update')
+export default class EclipseMarketplaceUpdate extends EclipseMarketplaceBase {
+ static category = 'activity'
+ static route = {
+ base: 'eclipse-marketplace/last-update',
+ pattern: ':name',
}
- static get examples() {
- return [
- {
- title: 'Eclipse Marketplace',
- namedParams: { name: 'notepad4e' },
- staticPreview: this.render({ date: new Date().getTime() }),
+ static openApi = {
+ '/eclipse-marketplace/last-update/{name}': {
+ get: {
+ summary: 'Eclipse Marketplace Last Update',
+ parameters: pathParams({
+ name: 'name',
+ example: 'notepad4e',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'updated' }
- }
-
- static render({ date }) {
- return {
- message: formatDate(date),
- color: ageColor(date),
- }
- }
+ static defaultBadgeData = { label: 'updated' }
async handle({ name }) {
const { marketplace } = await this.fetch({
@@ -50,6 +39,6 @@ module.exports = class EclipseMarketplaceUpdate extends EclipseMarketplaceBase {
schema: updateResponseSchema,
})
const date = 1000 * parseInt(marketplace.node.changed)
- return this.constructor.render({ date })
+ return renderDateBadge(date)
}
}
diff --git a/services/eclipse-marketplace/eclipse-marketplace-update.tester.js b/services/eclipse-marketplace/eclipse-marketplace-update.tester.js
index b59fc6b2493de..fd0d6e756bd51 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-update.tester.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-update.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isFormattedDate } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('last update date')
- .get('/notepad4e.json')
- .expectBadge({
- label: 'updated',
- message: isFormattedDate,
- })
+t.create('last update date').get('/notepad4e.json').expectBadge({
+ label: 'updated',
+ message: isFormattedDate,
+})
t.create('last update for unknown solution')
.get('/this-does-not-exist.json')
diff --git a/services/eclipse-marketplace/eclipse-marketplace-version.service.js b/services/eclipse-marketplace/eclipse-marketplace-version.service.js
index 5d35707b6dfc1..9da479366ff28 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-version.service.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-version.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const EclipseMarketplaceBase = require('./eclipse-marketplace-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
const versionResponseSchema = Joi.object({
marketplace: Joi.object({
@@ -12,28 +11,26 @@ const versionResponseSchema = Joi.object({
}),
}).required()
-module.exports = class EclipseMarketplaceVersion extends EclipseMarketplaceBase {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return this.buildRoute('eclipse-marketplace/v')
+export default class EclipseMarketplaceVersion extends EclipseMarketplaceBase {
+ static category = 'version'
+ static route = {
+ base: 'eclipse-marketplace/v',
+ pattern: ':name',
}
- static get examples() {
- return [
- {
- title: 'Eclipse Marketplace',
- namedParams: { name: 'notepad4e' },
- staticPreview: this.render({ version: '1.0.1' }),
+ static openApi = {
+ '/eclipse-marketplace/v/{name}': {
+ get: {
+ summary: 'Eclipse Marketplace Version',
+ parameters: pathParams({
+ name: 'name',
+ example: 'notepad4e',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'eclipse marketplace' }
- }
+ static defaultBadgeData = { label: 'eclipse marketplace' }
static render({ version }) {
return renderVersionBadge({ version })
diff --git a/services/eclipse-marketplace/eclipse-marketplace-version.tester.js b/services/eclipse-marketplace/eclipse-marketplace-version.tester.js
index 863c6863e5270..c47a95c8eea68 100644
--- a/services/eclipse-marketplace/eclipse-marketplace-version.tester.js
+++ b/services/eclipse-marketplace/eclipse-marketplace-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('marketplace version')
- .get('/notepad4e.json')
- .expectBadge({
- label: 'eclipse marketplace',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('marketplace version').get('/notepad4e.json').expectBadge({
+ label: 'eclipse marketplace',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('last update for unknown solution')
.get('/this-does-not-exist.json')
diff --git a/services/ecologi/ecologi-carbon.service.js b/services/ecologi/ecologi-carbon.service.js
new file mode 100644
index 0000000000000..3f90ad1c32879
--- /dev/null
+++ b/services/ecologi/ecologi-carbon.service.js
@@ -0,0 +1,48 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const apiSchema = Joi.object({
+ total: Joi.number().positive().required(),
+}).required()
+
+export default class EcologiCarbonOffset extends BaseJsonService {
+ static category = 'other'
+ static route = { base: 'ecologi/carbon', pattern: ':username' }
+ static openApi = {
+ '/ecologi/carbon/{username}': {
+ get: {
+ summary: 'Ecologi (Carbon Offset)',
+ parameters: pathParams({
+ name: 'username',
+ example: 'ecologi',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'carbon offset' }
+
+ static render({ count }) {
+ const tonnes = metric(count)
+ return { message: `${tonnes} tonnes`, color: floorCount(count, 0.5, 1, 5) }
+ }
+
+ async fetch({ username }) {
+ const url = `https://public.ecologi.com/users/${username}/carbon-offset`
+ return this._requestJson({
+ url,
+ schema: apiSchema,
+ httpErrors: {
+ 404: 'username not found',
+ },
+ })
+ }
+
+ async handle({ username }) {
+ const { total } = await this.fetch({ username })
+
+ return this.constructor.render({ count: total })
+ }
+}
diff --git a/services/ecologi/ecologi-carbon.tester.js b/services/ecologi/ecologi-carbon.tester.js
new file mode 100644
index 0000000000000..16f0b1fb7ae46
--- /dev/null
+++ b/services/ecologi/ecologi-carbon.tester.js
@@ -0,0 +1,16 @@
+import { createServiceTester } from '../tester.js'
+import { isMetricWithPattern } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('request for existing username')
+ .get('/ecologi.json')
+ .expectBadge({
+ label: 'carbon offset',
+ message: isMetricWithPattern(/ tonnes/),
+ })
+
+t.create('invalid username').get('/non-existent-username.json').expectBadge({
+ label: 'carbon offset',
+ message: 'username not found',
+ color: 'red',
+})
diff --git a/services/ecologi/ecologi-trees.service.js b/services/ecologi/ecologi-trees.service.js
new file mode 100644
index 0000000000000..724a4105528a3
--- /dev/null
+++ b/services/ecologi/ecologi-trees.service.js
@@ -0,0 +1,48 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const apiSchema = Joi.object({
+ total: nonNegativeInteger,
+}).required()
+
+export default class EcologiTrees extends BaseJsonService {
+ static category = 'other'
+ static route = { base: 'ecologi/trees', pattern: ':username' }
+ static openApi = {
+ '/ecologi/trees/{username}': {
+ get: {
+ summary: 'Ecologi (Trees)',
+ parameters: pathParams({
+ name: 'username',
+ example: 'ecologi',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'trees' }
+
+ static render({ count }) {
+ return { message: metric(count), color: floorCount(count, 10, 50, 100) }
+ }
+
+ async fetch({ username }) {
+ const url = `https://public.ecologi.com/users/${username}/trees`
+ return this._requestJson({
+ url,
+ schema: apiSchema,
+ httpErrors: {
+ 404: 'username not found',
+ },
+ })
+ }
+
+ async handle({ username }) {
+ const { total } = await this.fetch({ username })
+
+ return this.constructor.render({ count: total })
+ }
+}
diff --git a/services/ecologi/ecologi-trees.tester.js b/services/ecologi/ecologi-trees.tester.js
new file mode 100644
index 0000000000000..4aafb00ffb58e
--- /dev/null
+++ b/services/ecologi/ecologi-trees.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('request for existing username').get('/ecologi.json').expectBadge({
+ label: 'trees',
+ message: isMetric,
+})
+
+t.create('request for existing username')
+ .get('/ecologi.json')
+ .intercept(nock =>
+ nock('https://public.ecologi.com')
+ .get('/users/ecologi/trees')
+ .reply(200, { total: 50 }),
+ )
+ .expectBadge({
+ label: 'trees',
+ message: '50',
+ color: 'green',
+ })
+
+t.create('invalid username')
+ .get('/non-existent-username.json')
+ .expectBadge({ label: 'trees', message: 'username not found', color: 'red' })
diff --git a/services/elm-package/elm-package.service.js b/services/elm-package/elm-package.service.js
index 63918121a8d66..efa97a03abfec 100644
--- a/services/elm-package/elm-package.service.js
+++ b/services/elm-package/elm-package.service.js
@@ -1,39 +1,32 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { semver } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { semver } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({ version: semver }).required()
-module.exports = class ElmPackage extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'elm-package/v',
- pattern: ':user/:packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Elm package',
- namedParams: { user: 'elm', packageName: 'core' },
- staticPreview: this.render({ version: '1.0.2' }),
+export default class ElmPackage extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'elm-package/v', pattern: ':user/:packageName' }
+ static openApi = {
+ '/elm-package/v/{user}/{packageName}': {
+ get: {
+ summary: 'Elm package',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'elm',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'elm package',
- }
- }
+ static defaultBadgeData = { label: 'elm package' }
static render(props) {
return renderVersionBadge(props)
@@ -44,7 +37,7 @@ module.exports = class ElmPackage extends BaseJsonService {
const { version } = await this._requestJson({
schema,
url,
- errorMessages: {
+ httpErrors: {
404: 'package not found',
},
})
diff --git a/services/elm-package/elm-package.tester.js b/services/elm-package/elm-package.tester.js
index d432de124705f..b1c07737a607c 100644
--- a/services/elm-package/elm-package.tester.js
+++ b/services/elm-package/elm-package.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets the package version of elm/core')
.get('/elm/core.json')
diff --git a/services/endpoint-common.js b/services/endpoint-common.js
index 1cbe7b559a4bd..c316eaa33b1b5 100644
--- a/services/endpoint-common.js
+++ b/services/endpoint-common.js
@@ -1,26 +1,33 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to endpoint badges.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
-const validate = require('../core/base-service/validate')
-const { InvalidResponse } = require('.')
+import Joi from 'joi'
+import validate from '../core/base-service/validate.js'
+import { InvalidResponse } from './index.js'
const optionalStringWhenNamedLogoPresent = Joi.alternatives().conditional(
'namedLogo',
{
is: Joi.string().required(),
then: Joi.string(),
- }
+ },
)
const optionalNumberWhenAnyLogoPresent = Joi.alternatives()
.conditional('namedLogo', { is: Joi.string().required(), then: Joi.number() })
.conditional('logoSvg', { is: Joi.string().required(), then: Joi.number() })
+/**
+ * Joi schema for validating endpoint.
+ *
+ * @type {Joi}
+ */
const endpointSchema = Joi.object({
schemaVersion: 1,
- label: Joi.string()
- .allow('')
- .required(),
+ label: Joi.string().allow('').required(),
message: Joi.string().required(),
color: Joi.string(),
labelColor: Joi.string(),
@@ -28,23 +35,34 @@ const endpointSchema = Joi.object({
namedLogo: Joi.string(),
logoSvg: Joi.string(),
logoColor: optionalStringWhenNamedLogoPresent,
- logoWidth: optionalNumberWhenAnyLogoPresent,
- logoPosition: optionalNumberWhenAnyLogoPresent,
+ logoSize: optionalStringWhenNamedLogoPresent,
style: Joi.string(),
- cacheSeconds: Joi.number()
- .integer()
- .min(0),
+ cacheSeconds: Joi.number().integer().min(0),
+ /*
+ Retained for legacy compatibility
+ Although this does nothing,
+ passing it should not throw an error
+ */
+ logoPosition: optionalNumberWhenAnyLogoPresent,
+ logoWidth: optionalNumberWhenAnyLogoPresent,
})
// `namedLogo` or `logoSvg`; not both.
.oxor('namedLogo', 'logoSvg')
.required()
-// Strictly validate according to the endpoint schema. This rejects unknown /
-// invalid keys. Optionally it prints those keys in the message in order to
-// provide detailed feedback.
+/**
+ * Validate the data according to the endpoint schema.
+ *
+ * @param {object} data Object containing the data for validation
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} [attrs.prettyErrorMessage] If provided then error message is set to this value
+ * @param {boolean} [attrs.includeKeys] If true then includes error details in error message, defaults to false
+ * @throws {InvalidResponse|Error} Error if Joi validation fails due to invalid or no schema
+ * @returns {object} Value if Joi validation is success
+ */
function validateEndpointData(
data,
- { prettyErrorMessage = 'invalid response data', includeKeys = false } = {}
+ { prettyErrorMessage = 'invalid response data', includeKeys = false } = {},
) {
return validate(
{
@@ -53,23 +71,35 @@ function validateEndpointData(
includeKeys,
traceErrorMessage: 'Response did not match schema',
traceSuccessMessage: 'Response after validation',
- allowAndStripUnknownKeys: false,
},
data,
- endpointSchema
+ endpointSchema,
)
}
const anySchema = Joi.any()
+/**
+ * Fetches data from the endpoint and validates the data.
+ *
+ * @param {object} serviceInstance Instance of Endpoint class
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} attrs.url Endpoint URL
+ * @param {object} attrs.httpErrors Object containing error messages for different error codes
+ * @param {string} attrs.validationPrettyErrorMessage If provided then the error message is set to this value
+ * @param {boolean} attrs.includeKeys If true then includes error details in error message
+ * @returns {object} Data fetched from endpoint
+ */
async function fetchEndpointData(
serviceInstance,
- { url, errorMessages, validationPrettyErrorMessage, includeKeys }
+ { url, httpErrors, validationPrettyErrorMessage, includeKeys },
) {
const json = await serviceInstance._requestJson({
schema: anySchema,
url,
- errorMessages,
+ httpErrors,
+ logErrors: [],
+ options: { decompress: true, timeout: { request: 3500 } },
})
return validateEndpointData(json, {
prettyErrorMessage: validationPrettyErrorMessage,
@@ -77,7 +107,4 @@ async function fetchEndpointData(
})
}
-module.exports = {
- validateEndpointData,
- fetchEndpointData,
-}
+export { validateEndpointData, fetchEndpointData }
diff --git a/services/endpoint/endpoint-redirect.service.js b/services/endpoint/endpoint-redirect.service.js
index 4501e3e6a378c..9354fecaaaa2a 100644
--- a/services/endpoint/endpoint-redirect.service.js
+++ b/services/endpoint/endpoint-redirect.service.js
@@ -1,13 +1,12 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = redirector({
+export default retiredService({
category: 'other',
+ label: 'endpoint',
route: {
base: 'badge/endpoint',
pattern: '',
},
- transformPath: () => '/endpoint',
- dateAdded: new Date('2019-02-19'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
})
diff --git a/services/endpoint/endpoint-redirect.tester.js b/services/endpoint/endpoint-redirect.tester.js
index 097c083fe4021..153cd6074cf75 100644
--- a/services/endpoint/endpoint-redirect.tester.js
+++ b/services/endpoint/endpoint-redirect.tester.js
@@ -1,13 +1,14 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'EndpointRedirect',
title: 'EndpointRedirect',
pathPrefix: '/badge/endpoint',
-}))
+})
t.create('Build: default branch')
- .get('.svg?url=https://example.com/badge.json')
- .expectRedirect('/endpoint.svg?url=https://example.com/badge.json')
+ .get('.json?url=https://example.com/badge.json')
+ .expectBadge({
+ label: 'endpoint',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/endpoint/endpoint.service.js b/services/endpoint/endpoint.service.js
index 77afe2d673a20..583f5b2203f7a 100644
--- a/services/endpoint/endpoint.service.js
+++ b/services/endpoint/endpoint.service.js
@@ -1,41 +1,147 @@
-'use strict'
-
-const { URL } = require('url')
-const Joi = require('@hapi/joi')
-const { errorMessages } = require('../dynamic-common')
-const { optionalUrl } = require('../validators')
-const { fetchEndpointData } = require('../endpoint-common')
-const { BaseJsonService, InvalidParameter } = require('..')
+import { URL } from 'url'
+import Joi from 'joi'
+import configModule from 'config'
+import { httpErrors } from '../dynamic-common.js'
+import { url } from '../validators.js'
+import { fetchEndpointData } from '../endpoint-common.js'
+import { BaseJsonService, InvalidParameter, queryParams } from '../index.js'
const blockedDomains = ['github.com', 'shields.io']
const queryParamSchema = Joi.object({
- url: optionalUrl.required(),
+ url,
}).required()
-module.exports = class Endpoint extends BaseJsonService {
- static get category() {
- return 'dynamic'
- }
+const description = `
+Using the endpoint badge, you can provide content for a badge through
+a JSON endpoint. The content can be prerendered, or generated on the
+fly. To strike a balance between responsiveness and bandwidth
+utilization on one hand, and freshness on the other, cache behavior is
+configurable, subject to the Shields minimum. The endpoint URL is
+provided to Shields through the query string. Shields fetches it and
+formats the badge.
- static get route() {
- return {
- base: 'endpoint',
- pattern: '',
- queryParamSchema,
- }
- }
+The endpoint badge takes a single required query param: url, which is the URL to your JSON endpoint.
+
+A collection of APIs compatible with Shields.io's endpoint badge is available here .
+
+
+
Example JSON Endpoint Response
+
{ "schemaVersion": 1, "label": "hello", "message": "sweet world", "color": "orange" }
+
Example Shields Response
+
+
+
+
Schema
+
+
+
+ Property
+ Description
+
+
+ schemaVersion
+ Required. Always the number 1.
+
+
+ label
+
+ Required. The left text, or the empty string to omit the left side of
+ the badge. This can be overridden by the query string.
+
+
+
+ message
+ Required. Can't be empty. The right text.
+
+
+ color
+
+ Default: lightgrey. The right color. Supports the eight
+ named colors above, as well as hex, rgb, rgba, hsl, hsla and css named
+ colors. This can be overridden by the query string.
+
+
+
+ labelColor
+
+ Default: grey. The left color. This can be overridden by
+ the query string.
+
+
+
+ isError
+
+ Default: false. true to treat this as an
+ error badge. This prevents the user from overriding the color. In the
+ future, it may affect cache behavior.
+
+
+
+ namedLogo
+
+ Default: none. One of the
+ simple-icons slugs. Can be
+ overridden by the query string.
+
+
+
+ logoSvg
+ Default: none. An SVG string containing a custom logo.
+
+
+ logoColor
+
+ Default: none. Same meaning as the query string. Can be overridden by
+ the query string. Only works for simple-icons logos.
+
+
+
+ logoSize
+
+ Default: none. Make icons adaptively resize by setting auto.
+ Useful for some wider logos like amd and amg.
+ Supported for simple-icons logos only.
+
+
+
+ style
+
+ Default: flat. The default template to use. Can be
+ overridden by the query string.
+
+
+
+
+
`
- static get _cacheLength() {
- return 300
+export default class Endpoint extends BaseJsonService {
+ static category = 'dynamic'
+
+ static route = {
+ base: 'endpoint',
+ pattern: '',
+ queryParamSchema,
}
- static get defaultBadgeData() {
- return {
- label: 'custom badge',
- }
+ static openApi = {
+ '/endpoint': {
+ get: {
+ summary: 'Endpoint Badge',
+ description,
+ parameters: queryParams({
+ name: 'url',
+ description: 'The URL to your JSON endpoint',
+ required: true,
+ example: 'https://shields.redsparr0w.com/2473/monday',
+ }),
+ },
+ },
}
+ static _cacheLength = 300
+ static defaultBadgeData = { label: 'custom badge' }
+
static render({
isError,
label,
@@ -45,8 +151,7 @@ module.exports = class Endpoint extends BaseJsonService {
namedLogo,
logoSvg,
logoColor,
- logoWidth,
- logoPosition,
+ logoSize,
style,
cacheSeconds,
}) {
@@ -59,16 +164,32 @@ module.exports = class Endpoint extends BaseJsonService {
namedLogo,
logoSvg,
logoColor,
- logoWidth,
- logoPosition,
+ logoSize,
style,
- cacheSeconds,
+ // don't allow the user to set cacheSeconds any shorter than this._cacheLength
+ cacheSeconds: Math.max(
+ ...[this._cacheLength, cacheSeconds].filter(x => x !== undefined),
+ ),
}
}
+ constructor(...args) {
+ super(...args)
+ const config = configModule.util.toObject()
+ this._allowUnsecuredEndpointRequests =
+ config?.public?.allowUnsecuredEndpointRequests || false
+ }
+
async handle(namedParams, { url }) {
- const { protocol, hostname } = new URL(url)
- if (protocol !== 'https:') {
+ let protocol, hostname
+ try {
+ const parsedUrl = new URL(url)
+ protocol = parsedUrl.protocol
+ hostname = parsedUrl.hostname
+ } catch (e) {
+ throw new InvalidParameter({ prettyMessage: 'invalid url' })
+ }
+ if (protocol !== 'https:' && !this._allowUnsecuredEndpointRequests) {
throw new InvalidParameter({ prettyMessage: 'please use https' })
}
if (blockedDomains.some(domain => hostname.endsWith(domain))) {
@@ -77,7 +198,7 @@ module.exports = class Endpoint extends BaseJsonService {
const validated = await fetchEndpointData(this, {
url,
- errorMessages,
+ httpErrors,
validationPrettyErrorMessage: 'invalid properties',
includeKeys: true,
})
diff --git a/services/endpoint/endpoint.spec.js b/services/endpoint/endpoint.spec.js
new file mode 100644
index 0000000000000..cfccd918aa347
--- /dev/null
+++ b/services/endpoint/endpoint.spec.js
@@ -0,0 +1,58 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import nock from 'nock'
+import configModule from 'config'
+import { defaultContext } from '../test-helpers.js'
+import { InvalidParameter } from '../index.js'
+import Endpoint from './endpoint.service.js'
+
+describe('Endpoint', function () {
+ afterEach(function () {
+ sinon.restore()
+ })
+ it('allows unsecured endpoint when config enabled', async function () {
+ nock('http://example.com').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'unsecured',
+ message: 'allowed',
+ })
+
+ sinon.stub(configModule.util, 'toObject').returns({
+ public: { allowUnsecuredEndpointRequests: true },
+ })
+
+ const endpoint = new Endpoint(defaultContext, {
+ handleInternalErrors: false,
+ })
+ const result = await endpoint.handle(
+ {},
+ { url: 'http://example.com/badge' },
+ )
+ expect(result).to.include({ label: 'unsecured', message: 'allowed' })
+ })
+
+ it('blocks unsecured endpoint when config disabled', async function () {
+ nock('http://example.com').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'unsecured',
+ message: 'allowed',
+ })
+
+ sinon.stub(configModule.util, 'toObject').returns({
+ public: { allowUnsecuredEndpointRequests: false },
+ })
+
+ const endpoint = new Endpoint(defaultContext, {
+ handleInternalErrors: false,
+ })
+ let error
+ try {
+ await endpoint.handle({}, { url: 'http://example.com/badge' })
+ } catch (e) {
+ error = e
+ }
+ expect(error)
+ .to.be.instanceOf(InvalidParameter)
+ .to.have.property('prettyMessage', 'please use https')
+ })
+})
diff --git a/services/endpoint/endpoint.tester.js b/services/endpoint/endpoint.tester.js
index 43b9ca4fc78d2..798ee4d10a5ad 100644
--- a/services/endpoint/endpoint.tester.js
+++ b/services/endpoint/endpoint.tester.js
@@ -1,34 +1,30 @@
-'use strict'
-
-const { expect } = require('chai')
-const { getShieldsIcon } = require('../../lib/logos')
-const t = (module.exports = require('../tester').createServiceTester())
+import zlib from 'zlib'
+import { expect } from 'chai'
+import { getSimpleIcon } from '../../lib/logos.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Valid schema')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ }),
)
.expectBadge({ label: '', message: 'yo' })
t.create('color and labelColor')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- color: '#f0dcc3',
- labelColor: '#e6e6fa',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ color: '#f0dcc3',
+ labelColor: '#e6e6fa',
+ }),
)
.expectBadge({
label: 'hey',
@@ -40,14 +36,12 @@ t.create('color and labelColor')
t.create('style')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- color: '#99c',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ color: '#99c',
+ }),
)
.expectBadge({
label: 'hey',
@@ -60,224 +54,231 @@ t.create('style')
t.create('named logo')
.get('.svg?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- namedLogo: 'npm',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ namedLogo: 'npm',
+ }),
)
.after((err, res, body) => {
expect(err).not.to.be.ok
- expect(body).to.include(getShieldsIcon({ name: 'npm' }))
+ expect(body).to.include(getSimpleIcon({ name: 'npm' }))
})
t.create('named logo with color')
.get('.svg?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- namedLogo: 'npm',
- logoColor: 'blue',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ namedLogo: 'github',
+ logoColor: 'blue',
+ }),
)
.after((err, res, body) => {
expect(err).not.to.be.ok
- expect(body).to.include(getShieldsIcon({ name: 'npm', color: 'blue' }))
+ expect(body).to.include(getSimpleIcon({ name: 'github', color: 'blue' }))
+ })
+
+t.create('named logo with size')
+ .get('.svg?url=https://example.com/badge')
+ .intercept(nock =>
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ namedLogo: 'github',
+ logoSize: 'auto',
+ }),
+ )
+ .after((err, res, body) => {
+ expect(err).not.to.be.ok
+ expect(body).to.include(getSimpleIcon({ name: 'github', size: 'auto' }))
})
const logoSvg = Buffer.from(
- getShieldsIcon({ name: 'npm' }).replace('data:image/svg+xml;base64,', ''),
- 'base64'
+ getSimpleIcon({ name: 'npm' }).replace('data:image/svg+xml;base64,', ''),
+ 'base64',
).toString('ascii')
t.create('custom svg logo')
.get('.svg?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- logoSvg,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ logoSvg,
+ }),
)
.after((err, res, body) => {
expect(err).not.to.be.ok
- expect(body).to.include(getShieldsIcon({ name: 'npm' }))
+ expect(body).to.include(getSimpleIcon({ name: 'npm' }))
})
+// The logoWidth param was removed, but passing it should not
+// throw a validation error. It should just do nothing.
t.create('logoWidth')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- logoSvg,
- logoWidth: 30,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ logoSvg,
+ logoWidth: 30,
+ }),
)
.expectBadge({
label: 'hey',
message: 'yo',
- logoWidth: 30,
})
-t.create('Invalid schema)')
+// The logoPosition param was removed, but passing it should not
+// throw a validation error. It should just do nothing.
+t.create('logoPosition')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: -1,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ logoSvg,
+ logoPosition: 30,
+ }),
+ )
+ .expectBadge({
+ label: 'hey',
+ message: 'yo',
+ })
+
+t.create('Invalid schema')
+ .get('.json?url=https://example.com/badge')
+ .intercept(nock =>
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: -1,
+ }),
)
.expectBadge({
label: 'custom badge',
message: 'invalid properties: schemaVersion, label, message',
})
-t.create('Invalid schema)')
+t.create('Valid schema with extra keys')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: 'hey',
- message: 'yo',
- extra: 'keys',
- bogus: true,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: 'hey',
+ message: 'yo',
+ extra: 'keys',
+ bogus: true,
+ }),
)
.expectBadge({
- label: 'custom badge',
- message: 'invalid properties: extra, bogus',
+ label: 'hey',
+ message: 'yo',
})
t.create('User color overrides success color')
.get('.json?url=https://example.com/badge&color=101010')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- color: 'blue',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ color: 'blue',
+ }),
)
.expectBadge({ label: '', message: 'yo', color: '#101010' })
t.create('User legacy color overrides success color')
.get('.json?url=https://example.com/badge&colorB=101010')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- color: 'blue',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ color: 'blue',
+ }),
)
.expectBadge({ label: '', message: 'yo', color: '#101010' })
t.create('User color does not override error color')
.get('.json?url=https://example.com/badge&color=101010')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- isError: true,
- label: 'something is',
- message: 'not right',
- color: 'red',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ isError: true,
+ label: 'something is',
+ message: 'not right',
+ color: 'red',
+ }),
)
.expectBadge({ label: 'something is', message: 'not right', color: 'red' })
t.create('User legacy color does not override error color')
.get('.json?url=https://example.com/badge&colorB=101010')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- isError: true,
- label: 'something is',
- message: 'not right',
- color: 'red',
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ isError: true,
+ label: 'something is',
+ message: 'not right',
+ color: 'red',
+ }),
)
.expectBadge({ label: 'something is', message: 'not right', color: 'red' })
t.create('cacheSeconds')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- cacheSeconds: 500,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ cacheSeconds: 500,
+ }),
)
- .expectHeader('cache-control', 'max-age=500')
+ .expectHeader('cache-control', 'max-age=500, s-maxage=500')
t.create('user can override service cacheSeconds')
.get('.json?url=https://example.com/badge&cacheSeconds=1000')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- cacheSeconds: 500,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ cacheSeconds: 500,
+ }),
)
- .expectHeader('cache-control', 'max-age=1000')
+ .expectHeader('cache-control', 'max-age=1000, s-maxage=1000')
t.create('user does not override longer service cacheSeconds')
.get('.json?url=https://example.com/badge&cacheSeconds=450')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- cacheSeconds: 500,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ cacheSeconds: 500,
+ }),
)
- .expectHeader('cache-control', 'max-age=500')
+ .expectHeader('cache-control', 'max-age=500, s-maxage=500')
t.create('cacheSeconds does not override longer Shields default')
.get('.json?url=https://example.com/badge')
.intercept(nock =>
- nock('https://example.com/')
- .get('/badge')
- .reply(200, {
- schemaVersion: 1,
- label: '',
- message: 'yo',
- cacheSeconds: 10,
- })
+ nock('https://example.com/').get('/badge').reply(200, {
+ schemaVersion: 1,
+ label: '',
+ message: 'yo',
+ cacheSeconds: 10,
+ }),
)
- .expectHeader('cache-control', 'max-age=300')
+ .expectHeader('cache-control', 'max-age=300, s-maxage=300')
t.create('Bad scheme')
.get('.json?url=http://example.com/badge')
@@ -288,9 +289,30 @@ t.create('Blocked domain')
.expectBadge({ label: 'custom badge', message: 'domain is blocked' })
// https://github.com/badges/shields/issues/3780
-t.create('Invalid url')
- .get('.json?url=https:/')
+t.create('Invalid url (1)').get('.json?url=https:/').expectBadge({
+ label: 'custom badge',
+ message: 'invalid query parameter: url',
+})
+
+t.create('Invalid url (2)')
+ .get('.json?url=https%3A//shields.io%foo')
.expectBadge({
label: 'custom badge',
- message: 'invalid query parameter: url',
+ message: 'invalid url',
})
+
+// https://github.com/badges/shields/issues/5868
+t.create('gzipped endpoint')
+ .get('.json?url=https://example.com/badge')
+ .intercept(nock =>
+ nock('https://example.com/')
+ .get('/badge')
+ .reply(
+ 200,
+ zlib.gzipSync(
+ JSON.stringify({ schemaVersion: 1, label: '', message: 'yo' }),
+ ),
+ { 'Content-Encoding': 'gzip' },
+ ),
+ )
+ .expectBadge({ label: '', message: 'yo' })
diff --git a/services/f-droid/f-droid.service.js b/services/f-droid/f-droid.service.js
index ae5f16f2592de..d662d545b10aa 100644
--- a/services/f-droid/f-droid.service.js
+++ b/services/f-droid/f-droid.service.js
@@ -1,120 +1,95 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { addv } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const { BaseYamlService, InvalidResponse } = require('..')
+import Joi from 'joi'
+import {
+ optionalNonNegativeInteger,
+ nonNegativeInteger,
+ optionalUrl,
+} from '../validators.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
const schema = Joi.object({
- CurrentVersion: Joi.alternatives()
- .try(Joi.string(), Joi.number())
- .required(),
+ packageName: Joi.string().required(),
+ suggestedVersionCode: optionalNonNegativeInteger,
+ packages: Joi.array().items({
+ versionName: Joi.string().required(),
+ versionCode: nonNegativeInteger,
+ }),
}).required()
const queryParamSchema = Joi.object({
- metadata_format: Joi.string().valid('yml', 'txt'),
+ baseUrl: optionalUrl,
+ include_prereleases: Joi.equal(''),
}).required()
-module.exports = class FDroid extends BaseYamlService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'f-droid/v',
- pattern: ':appId',
- queryParamSchema,
- }
- }
+export default class FDroid extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'f-droid/v', pattern: ':appId', queryParamSchema }
+ static openApi = {
+ '/f-droid/v/{appId}': {
+ get: {
+ summary: 'F-Droid Version',
+ description: `
+ [F-Droid](https://f-droid.org/) is a catalogue of Open Source Android apps.
- static get examples() {
- return [
- {
- title: 'F-Droid',
- namedParams: { appId: 'org.thosp.yourlocalweather' },
- staticPreview: this.render({ version: '1.0' }),
- keywords: ['fdroid', 'android', 'app'],
+ This badge by default uses f-droid.org, but also supports custom repos.
+ `,
+ parameters: [
+ pathParam({
+ name: 'appId',
+ example: 'org.dystopia.email',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://apt.izzysoft.de/fdroid',
+ description:
+ 'URL of a third party F-Droid server. If the API is not located at root path, specify the additional path to the API.',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
},
- {
- title: 'F-Droid (explicit metadata format)',
- namedParams: { appId: 'org.dystopia.email' },
- queryParams: { metadata_format: 'yml' },
- staticPreview: this.render({ version: '1.2.1' }),
- keywords: ['fdroid', 'android', 'app'],
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'f-droid' }
+ },
}
- static render({ version }) {
- return {
- message: addv(version),
- color: versionColor(version),
- }
- }
+ static defaultBadgeData = { label: 'f-droid' }
- async fetchYaml(url, options) {
- const yaml = await this._requestYaml({
+ async fetch({ baseUrl, appId }) {
+ baseUrl = baseUrl.replace(/\/$/, '')
+ const url = `${baseUrl}/api/v1/packages/${appId}`
+ return this._requestJson({
schema,
- url: `${url}.yml`,
- ...options,
+ url,
+ httpErrors: {
+ 403: 'app not found',
+ 404: 'app not found',
+ },
})
- return { version: yaml.CurrentVersion }
}
- async fetchText(url, options) {
- const { buffer } = await this._request({
- url: `${url}.txt`,
- ...options,
- })
- const metadata = buffer.toString()
- // we assume the layout as provided here:
- // https://gitlab.com/fdroid/fdroiddata/raw/master/metadata/axp.tool.apkextractor.txt
- const positionOfCurrentVersionAtEndOfTheFile = metadata.lastIndexOf(
- 'Current Version:'
- ) // credits: https://stackoverflow.com/a/11134049
- const lastVersion = metadata.substring(
- positionOfCurrentVersionAtEndOfTheFile
+ transform({ json, suggested }) {
+ const svc = suggested && json.suggestedVersionCode
+ const packages = (json.packages || []).filter(
+ ({ versionCode }) => !svc || versionCode <= svc,
)
-
- const match = lastVersion.match(/^Current Version:\s*(.*?)\s*$/m)
- if (!match) {
- throw new InvalidResponse({
- prettyMessage: 'invalid response',
- underlyingError: new Error('could not find version on website'),
- })
+ if (packages.length === 0) {
+ throw new NotFound({ prettyMessage: 'no packages found' })
}
- return { version: match[1] }
+ const version = packages.reduce((a, b) =>
+ a.versionCode > b.versionCode ? a : b,
+ ).versionName
+ return { version }
}
- async handle({ appId }, { metadata_format: metadataFormat }) {
- const url = `https://gitlab.com/fdroid/fdroiddata/raw/master/metadata/${appId}`
- const fetchOpts = {
- options: {},
- errorMessages: {
- 404: 'app not found',
- },
- }
- const fetch = metadataFormat === 'yml' ? this.fetchYaml : this.fetchText
- let result
-
- try {
- // currently, we only use the txt format to the initial fetch because
- // there are more apps with that format but yml is now the standard format
- // on f-droid, so if txt is not found we look for yml as the fallback
- result = await fetch.call(this, url, fetchOpts)
- } catch (error) {
- if (metadataFormat) {
- // if the format was specified it doesn't make the fallback request
- throw error
- }
- result = await this.fetchYaml(url, fetchOpts)
- }
-
- return this.constructor.render(result)
+ async handle(
+ { appId },
+ { baseUrl = 'https://f-droid.org', include_prereleases: includePre },
+ ) {
+ const json = await this.fetch({ baseUrl, appId })
+ const suggested = includePre === undefined
+ const { version } = this.transform({ json, suggested })
+ return renderVersionBadge({ version })
}
}
diff --git a/services/f-droid/f-droid.tester.js b/services/f-droid/f-droid.tester.js
index 39203ca4953ab..3c8074d9cedd2 100644
--- a/services/f-droid/f-droid.tester.js
+++ b/services/f-droid/f-droid.tester.js
@@ -1,218 +1,144 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'f-droid',
title: 'F-Droid',
-}))
-
-const testString = `
-Categories:System
-License:MIT
-Web Site:https://github.com/axxapy/apkExtractor/blob/HEAD/README.md
-Source Code:https://github.com/axxapy/apkExtractor
-Issue Tracker:https://github.com/axxapy/apkExtractor/issues
-
-Auto Name:Apk Extractor
-Summary:Get APK files from installed apps
-Description:
-Extract APKs from your device, even if installed from the Playstore. Root access
-is required for paid apps.
-
-* Fast and easy to use.
-* Extracts almost all applications, includes system applications.
-* ROOT access only required for extracting paid apps.
-* Apks will be saved in /sdcard/Download/Eimon/.
-* Provided Search option to search applications.
-* Compatible with latest version of Android 6.0
-* Saved apk format : AppPackageName.apk.
-Current Version:1.8
-
-Repo Type:git
-Repo:https://github.com/axxapy/apkExtractor
-
-Build:1.0,1
- commit=9b3b62c3ceda74b17eaa22c9e4f893aac10c4442
- gradle=yes
-
-Build:1.1,2
- commit=1.1
- gradle=yes
-
-Build:1.2,3
- disable=lintVitalRelease fails
- commit=1.2
- gradle=yes
-
-Build:1.3,4
- commit=1.3
- gradle=yes
-
-Build:1.4,5
- commit=1.4
- gradle=yes
-
-Auto Update Mode:Version %v
-Update Check Mode:Tags
-Current Version:1.4
-Current Version Code:5
-`
-const testYmlString = `
-Categories: System
-License: MIT
-WebSite: https://github.com/axxapy/apkExtractor/blob/HEAD/README.md
-SourceCode: https://github.com/axxapy/apkExtractor
-IssueTracker: https://github.com/axxapy/apkExtractor/issues
-
-AutoName: Apk Extractor
-Summary: Get APK files from installed apps
-Description: |-
- Extract APKs from your device, even if installed from the Playstore. Root access
- is required for paid apps.
-
- * Fast and easy to use.
- * Extracts almost all applications, includes system applications.
- * ROOT access only required for extracting paid apps.
- * Apk's will be saved in /sdcard/Download/Eimon/.
- * Provided Search option to search applications.
- * Compatible with latest version of Android 6.0
- * Saved apk format : AppPackageName.apk.
-
-RepoType: git
-Repo: https://github.com/axxapy/apkExtractor
-
-Builds:
- - versionName: '1.2'
- versionCode: 32
- commit: '0.32'
- subdir: app
- gradle:
- - yes
-
- - versionName: '1.4'
- versionCode: 33
- commit: '5'
- subdir: app
- gradle:
- - yes
-
-AutoUpdateMode: Version %v
-UpdateCheckMode: Tags
-CurrentVersion: 1.4
-CurrentVersionCode: 33
+})
+
+const testPkg = 'org.fdroid.fdroid.privileged'
+const testJson = `
+{
+ "packageName": "${testPkg}",
+ "suggestedVersionCode": 2090,
+ "packages": [
+ {
+ "versionName": "0.2.11",
+ "versionCode": 2110
+ },
+ {
+ "versionName": "0.2.10",
+ "versionCode": 2100
+ },
+ {
+ "versionName": "0.2.7",
+ "versionCode": 2070
+ }
+ ]
+}
`
-const base = 'https://gitlab.com'
-const path = '/fdroid/fdroiddata/raw/master/metadata/axp.tool.apkextractor'
-t.create('Package is found with default metadata format')
- .get('/v/axp.tool.apkextractor.json')
- .intercept(nock =>
- nock(base)
- .get(`${path}.txt`)
- .reply(200, testString)
- )
- .expectBadge({ label: 'f-droid', message: 'v1.4' })
+const base = 'https://f-droid.org/api/v1'
+const path = `/packages/${testPkg}`
-t.create('Package is found with fallback yml matadata format')
- .get('/v/axp.tool.apkextractor.json')
- .intercept(nock =>
- nock(base)
- .get(`${path}.txt`)
- .reply(404)
- )
- .intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(200, testYmlString)
- )
- .expectBadge({ label: 'f-droid', message: 'v1.4' })
+t.create('f-droid.org: Package is found')
+ .get(`/v/${testPkg}.json`)
+ .intercept(nock => nock(base).get(path).reply(200, testJson))
+ .expectBadge({ label: 'f-droid', message: 'v0.2.7' })
-t.create('Trailing 0 in yml format')
- .get('/v/axp.tool.apkextractor.json')
- .intercept(nock =>
- nock(base)
- .get(`${path}.txt`)
- .reply(404)
- )
- .intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(200, "CurrentVersion: '1.4000'")
- )
- .expectBadge({ label: 'f-droid', message: 'v1.4000' })
+t.create('f-droid.org: Package is found (pre-release)')
+ .get(`/v/${testPkg}.json?include_prereleases`)
+ .intercept(nock => nock(base).get(path).reply(200, testJson))
+ .expectBadge({ label: 'f-droid', message: 'v0.2.11' })
-t.create('Package is found with yml matadata format')
- .get('/v/axp.tool.apkextractor.json?metadata_format=yml')
- .intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(200, testYmlString)
- )
- .expectBadge({ label: 'f-droid', message: 'v1.4' })
+t.create('f-droid.org: Package is not found with 403')
+ .get(`/v/${testPkg}.json`)
+ .intercept(nock => nock(base).get(path).reply(403, 'some 403 text'))
+ .expectBadge({ label: 'f-droid', message: 'app not found' })
-t.create('Package is not found with "metadata_format" query parameter')
- .get('/v/axp.tool.apkextractor.json?metadata_format=yml')
+t.create('f-droid.org: Package is not found with 404')
+ .get('/v/io.shiels.does.not.exist.json')
.intercept(nock =>
nock(base)
- .get(`${path}.yml`)
- .reply(404)
+ .get('/packages/io.shiels.does.not.exist')
+ .reply(404, 'some 404 text'),
)
.expectBadge({ label: 'f-droid', message: 'app not found' })
-t.create('Package is found yml matadata format with missing "CurrentVersion"')
- .get('/v/axp.tool.apkextractor.json?metadata_format=yml')
+t.create(
+ 'f-droid.org: Package is not found with no packages available (empty array)"',
+)
+ .get(`/v/${testPkg}.json`)
.intercept(nock =>
nock(base)
- .get(`${path}.yml`)
- .reply(200, 'Categories: System')
+ .get(path)
+ .reply(200, `{"packageName":"${testPkg}","packages":[]}`),
)
- .expectBadge({ label: 'f-droid', message: 'invalid response data' })
+ .expectBadge({ label: 'f-droid', message: 'no packages found' })
-t.create('Package is found with bad yml matadata format')
- .get('/v/axp.tool.apkextractor.json?metadata_format=yml')
+t.create(
+ 'f-droid.org: Package is not found with no packages available (missing array)"',
+)
+ .get(`/v/${testPkg}.json`)
.intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(200, '.CurrentVersion: 1.4')
+ nock(base).get(path).reply(200, `{"packageName":"${testPkg}"}`),
)
- .expectBadge({ label: 'f-droid', message: 'invalid response data' })
+ .expectBadge({ label: 'f-droid', message: 'no packages found' })
-t.create('Package is not found')
- .get('/v/axp.tool.apkextractor.json')
- .intercept(nock =>
- nock(base)
- .get(`${path}.txt`)
- .reply(404)
+/* If this test fails, either the API has changed or the app was deleted. */
+t.create('f-droid.org: The real api did not change')
+ .get('/v/org.thosp.yourlocalweather.json')
+ .expectBadge({
+ label: 'f-droid',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
+const base2 = 'https://apt.izzysoft.de/fdroid/api/v1'
+const path2 = `/packages/${testPkg}`
+
+t.create('custom repo: Package is found')
+ .get(`/v/${testPkg}.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid`)
+ .intercept(nock => nock(base2).get(path2).reply(200, testJson))
+ .expectBadge({ label: 'f-droid', message: 'v0.2.7' })
+
+t.create('custom repo: Package is found (pre-release)')
+ .get(
+ `/v/${testPkg}.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid&include_prereleases`,
+ )
+ .intercept(nock => nock(base2).get(path2).reply(200, testJson))
+ .expectBadge({ label: 'f-droid', message: 'v0.2.11' })
+
+t.create('custom repo: Package is not found with 403')
+ .get(`/v/${testPkg}.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid`)
+ .intercept(nock => nock(base2).get(path2).reply(403, 'some 403 text'))
+ .expectBadge({ label: 'f-droid', message: 'app not found' })
+
+t.create('custom repo: Package is not found with 404')
+ .get(
+ '/v/io.shiels.does.not.exist.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid',
)
.intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(404)
+ nock(base2)
+ .get('/packages/io.shiels.does.not.exist')
+ .reply(404, 'some 404 text'),
)
.expectBadge({ label: 'f-droid', message: 'app not found' })
-t.create('The api changed')
- .get('/v/axp.tool.apkextractor.json?metadata_format=yml')
+t.create(
+ 'custom repo: Package is not found with no packages available (empty array)"',
+)
+ .get(`/v/${testPkg}.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid`)
.intercept(nock =>
- nock(base)
- .get(`${path}.yml`)
- .reply(200, '')
+ nock(base2)
+ .get(path2)
+ .reply(200, `{"packageName":"${testPkg}","packages":[]}`),
)
- .expectBadge({ label: 'f-droid', message: 'invalid response data' })
+ .expectBadge({ label: 'f-droid', message: 'no packages found' })
-t.create('Package is not found due invalid metadata format')
- .get('/v/axp.tool.apkextractor.json?metadata_format=xml')
- .expectBadge({
- label: 'f-droid',
- message: 'invalid query parameter: metadata_format',
- })
+t.create(
+ 'custom repo: Package is not found with no packages available (missing array)"',
+)
+ .get(`/v/${testPkg}.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid`)
+ .intercept(nock =>
+ nock(base2).get(path2).reply(200, `{"packageName":"${testPkg}"}`),
+ )
+ .expectBadge({ label: 'f-droid', message: 'no packages found' })
/* If this test fails, either the API has changed or the app was deleted. */
-t.create('The real api did not change')
- .get('/v/org.thosp.yourlocalweather.json')
- .timeout(10000)
+t.create('custom repo: The real api did not change')
+ .get(
+ '/v/com.looker.droidify.json?baseUrl=https%3A%2F%2Fapt.izzysoft.de%2Ffdroid',
+ )
.expectBadge({
label: 'f-droid',
message: isVPlusDottedVersionAtLeastOne,
diff --git a/services/factorio-mod-portal/factorio-mod-portal.service.js b/services/factorio-mod-portal/factorio-mod-portal.service.js
new file mode 100644
index 0000000000000..f8ab8eadea741
--- /dev/null
+++ b/services/factorio-mod-portal/factorio-mod-portal.service.js
@@ -0,0 +1,177 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import { nonNegativeInteger } from '../validators.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { renderVersionBadge } from '../version.js'
+
+const schema = Joi.object({
+ downloads_count: nonNegativeInteger,
+ releases: Joi.array()
+ .items(
+ Joi.object({
+ version: Joi.string().required(),
+ released_at: Joi.string().required(),
+ info_json: Joi.object({
+ factorio_version: Joi.string().required(),
+ }).required(),
+ }),
+ )
+ .min(1)
+ .required(),
+}).required()
+
+// Factorio Mod portal API
+// @see https://wiki.factorio.com/Mod_portal_API
+class BaseFactorioModPortalService extends BaseJsonService {
+ async fetch({ modName }) {
+ const { releases, downloads_count } = await this._requestJson({
+ schema,
+ url: `https://mods.factorio.com/api/mods/${modName}`,
+ httpErrors: {
+ 404: 'mod not found',
+ },
+ })
+
+ return {
+ downloads_count,
+ latest_release: releases[releases.length - 1],
+ }
+ }
+}
+
+// Badge for mod's latest updated version
+class FactorioModPortalLatestVersion extends BaseFactorioModPortalService {
+ static category = 'version'
+
+ static route = {
+ base: 'factorio-mod-portal/v',
+ pattern: ':modName',
+ }
+
+ static openApi = {
+ '/factorio-mod-portal/v/{modName}': {
+ get: {
+ summary: 'Factorio Mod Portal mod version',
+ parameters: pathParams({
+ name: 'modName',
+ example: 'rso-mod',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'latest version' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async handle({ modName }) {
+ const resp = await this.fetch({ modName })
+ return this.constructor.render({ version: resp.latest_release.version })
+ }
+}
+
+// Badge for mod's latest compatible Factorio version
+class FactorioModPortalFactorioVersion extends BaseFactorioModPortalService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'factorio-mod-portal/factorio-version',
+ pattern: ':modName',
+ }
+
+ static openApi = {
+ '/factorio-mod-portal/factorio-version/{modName}': {
+ get: {
+ summary: 'Factorio Mod Portal factorio versions',
+ parameters: pathParams({
+ name: 'modName',
+ example: 'rso-mod',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'factorio version' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async handle({ modName }) {
+ const resp = await this.fetch({ modName })
+ const version = resp.latest_release.info_json.factorio_version
+ return this.constructor.render({ version })
+ }
+}
+
+// Badge for mod's last updated date
+class FactorioModPortalLastUpdated extends BaseFactorioModPortalService {
+ static category = 'activity'
+
+ static route = {
+ base: 'factorio-mod-portal/last-updated',
+ pattern: ':modName',
+ }
+
+ static openApi = {
+ '/factorio-mod-portal/last-updated/{modName}': {
+ get: {
+ summary: 'Factorio Mod Portal last updated',
+ parameters: pathParams({
+ name: 'modName',
+ example: 'rso-mod',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ async handle({ modName }) {
+ const resp = await this.fetch({ modName })
+ return renderDateBadge(resp.latest_release.released_at)
+ }
+}
+
+// Badge for mod's total download count
+class FactorioModPortalDownloads extends BaseFactorioModPortalService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'factorio-mod-portal/dt',
+ pattern: ':modName',
+ }
+
+ static openApi = {
+ '/factorio-mod-portal/dt/{modName}': {
+ get: {
+ summary: 'Factorio Mod Portal downloads',
+ parameters: pathParams({
+ name: 'modName',
+ example: 'rso-mod',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ static render({ downloads }) {
+ return renderDownloadsBadge({ downloads })
+ }
+
+ async handle({ modName }) {
+ const resp = await this.fetch({ modName })
+ return this.constructor.render({ downloads: resp.downloads_count })
+ }
+}
+
+export {
+ FactorioModPortalLatestVersion,
+ FactorioModPortalLastUpdated,
+ FactorioModPortalFactorioVersion,
+ FactorioModPortalDownloads,
+}
diff --git a/services/factorio-mod-portal/factorio-mod-portal.tester.js b/services/factorio-mod-portal/factorio-mod-portal.tester.js
new file mode 100644
index 0000000000000..1c06399116d82
--- /dev/null
+++ b/services/factorio-mod-portal/factorio-mod-portal.tester.js
@@ -0,0 +1,47 @@
+import {
+ isVPlusDottedVersionNClauses,
+ isFormattedDate,
+ isMetric,
+} from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'factorio-mod-portal',
+ title: 'Factorio Mod Portal',
+})
+
+t.create('Latest Version (rso-mod, valid)').get('/v/rso-mod.json').expectBadge({
+ label: 'latest version',
+ message: isVPlusDottedVersionNClauses,
+})
+
+t.create('Latest Version (mod not found)')
+ .get('/v/mod-that-doesnt-exist.json')
+ .expectBadge({ label: 'latest version', message: 'mod not found' })
+
+t.create('Factorio Version (rso-mod, valid)')
+ .get('/factorio-version/rso-mod.json')
+ .expectBadge({
+ label: 'factorio version',
+ message: isVPlusDottedVersionNClauses,
+ })
+
+t.create('Factorio Version (mod not found)')
+ .get('/factorio-version/mod-that-doesnt-exist.json')
+ .expectBadge({ label: 'factorio version', message: 'mod not found' })
+
+t.create('Last Updated (rso-mod, valid)')
+ .get('/last-updated/rso-mod.json')
+ .expectBadge({ label: 'last updated', message: isFormattedDate })
+
+t.create('Last Updated (mod not found)')
+ .get('/last-updated/mod-that-doesnt-exist.json')
+ .expectBadge({ label: 'last updated', message: 'mod not found' })
+
+t.create('Downloads (rso-mod, valid)')
+ .get('/dt/rso-mod.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('Downloads (mod not found)')
+ .get('/dt/mod-that-doesnt-exist.json')
+ .expectBadge({ label: 'downloads', message: 'mod not found' })
diff --git a/services/fedora/fedora.service.js b/services/fedora/fedora.service.js
index 240df13c935c1..aa11ea521d291 100644
--- a/services/fedora/fedora.service.js
+++ b/services/fedora/fedora.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
version: Joi.string().required(),
@@ -11,42 +9,51 @@ const schema = Joi.object({
// No way to permalink to current "stable", https://pagure.io/mdapi/issue/69
const defaultBranch = 'rawhide'
-module.exports = class Fedora extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'fedora/v',
- pattern: ':packageName/:branch?',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Fedora package',
- namedParams: { packageName: 'rpm', branch: 'rawhide' },
- staticPreview: renderVersionBadge({ version: '4.14.2.1' }),
- documentation:
- 'See mdapi docs for information on valid branches.',
+const description =
+ 'See mdapi docs for information on valid branches.'
+
+export default class Fedora extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'fedora/v', pattern: ':packageName/:branch?' }
+ static openApi = {
+ '/fedora/v/{packageName}/{branch}': {
+ get: {
+ summary: 'Fedora package (with branch)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'packageName',
+ example: 'rpm',
+ },
+ {
+ name: 'branch',
+ example: 'rawhide',
+ },
+ ),
+ },
+ },
+ '/fedora/v/{packageName}': {
+ get: {
+ summary: 'Fedora package',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'rpm',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'fedora' }
- }
+ static defaultBadgeData = { label: 'fedora' }
async handle({ packageName, branch = defaultBranch }) {
const data = await this._requestJson({
schema,
url: `https://apps.fedoraproject.org/mdapi/${encodeURIComponent(
- branch
+ branch,
)}/pkg/${encodeURIComponent(packageName)}`,
- errorMessages: {
- 400: 'branch not found',
+ httpErrors: {
+ 400: 'branch or package not found',
},
})
return renderVersionBadge({ version: data.version })
diff --git a/services/fedora/fedora.tester.js b/services/fedora/fedora.tester.js
index 134b5e84a2595..4fbd9ae45ec29 100644
--- a/services/fedora/fedora.tester.js
+++ b/services/fedora/fedora.tester.js
@@ -1,9 +1,6 @@
-'use strict'
-
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Fedora package (default branch, valid)')
.get('/rpm.json')
@@ -12,10 +9,10 @@ t.create('Fedora package (default branch, valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
})
-t.create('Fedora package (not found)')
+t.create('Fedora package (package not found)')
.get('/not-a-package/rawhide.json')
- .expectBadge({ label: 'fedora', message: 'not found' })
+ .expectBadge({ label: 'fedora', message: 'branch or package not found' })
t.create('Fedora package (branch not found)')
.get('/not-a-package/not-a-branch.json')
- .expectBadge({ label: 'fedora', message: 'branch not found' })
+ .expectBadge({ label: 'fedora', message: 'branch or package not found' })
diff --git a/services/feedz/feedz.service.js b/services/feedz/feedz.service.js
new file mode 100644
index 0000000000000..25638174e24a5
--- /dev/null
+++ b/services/feedz/feedz.service.js
@@ -0,0 +1,84 @@
+import Joi from 'joi'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
+import { stripBuildMetadata, selectVersion } from '../nuget/nuget-helpers.js'
+import { renderVersionBadge } from '../version.js'
+
+const packagesSchema = Joi.object({
+ versions: Joi.array().items(Joi.string()).required(),
+}).required()
+
+class FeedzVersionService extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'feedz',
+ pattern: ':variant(v|vpre)/:organization/:repository/:packageName',
+ }
+
+ static openApi = {
+ '/feedz/{variant}/{organization}/{repository}/{packageName}': {
+ get: {
+ summary: 'Feedz Version',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'v',
+ description: 'version or version including pre-releases',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'organization',
+ example: 'shieldstests',
+ },
+ {
+ name: 'repository',
+ example: 'mongodb',
+ },
+ {
+ name: 'packageName',
+ example: 'MongoDB.Driver.Core',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'feedz',
+ }
+
+ packagesUrl({ organization, repository, packageName }) {
+ return `https://f.feedz.io/${organization}/${repository}/nuget/v3/packages/${packageName}/index.json`
+ }
+
+ transform({ versions, includePrereleases }) {
+ if (versions.length === 0) {
+ throw new NotFound({ prettyMessage: 'package not found' })
+ }
+ // Strip build metadata and select the appropriate version
+ const cleanedVersions = versions.map(v => stripBuildMetadata(v))
+ return selectVersion(cleanedVersions, includePrereleases)
+ }
+
+ async handle({ variant, organization, repository, packageName }) {
+ const includePrereleases = variant === 'vpre'
+ const url = this.packagesUrl({ organization, repository, packageName })
+ const json = await this._requestJson({
+ schema: packagesSchema,
+ url,
+ httpErrors: {
+ 404: 'repository or package not found',
+ },
+ })
+ const version = this.transform({
+ versions: json.versions,
+ includePrereleases,
+ })
+ return renderVersionBadge({
+ version,
+ defaultLabel: FeedzVersionService.defaultBadgeData.label,
+ })
+ }
+}
+
+export { FeedzVersionService }
diff --git a/services/feedz/feedz.service.spec.js b/services/feedz/feedz.service.spec.js
new file mode 100644
index 0000000000000..28e722b0e8c83
--- /dev/null
+++ b/services/feedz/feedz.service.spec.js
@@ -0,0 +1,66 @@
+import { test, given } from 'sazerac'
+import { FeedzVersionService } from './feedz.service.js'
+
+describe('Feedz service', function () {
+ test(FeedzVersionService.prototype.packagesUrl, () => {
+ given({
+ organization: 'shieldstests',
+ repository: 'public',
+ packageName: 'Shields.TestPackage',
+ }).expect(
+ 'https://f.feedz.io/shieldstests/public/nuget/v3/packages/Shields.TestPackage/index.json',
+ )
+ })
+
+ test(FeedzVersionService.prototype.transform, () => {
+ given({
+ versions: ['1.0.0'],
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ versions: ['1.0.0', '1.0.1'],
+ includePrereleases: false,
+ }).expect('1.0.1')
+ given({
+ versions: ['1.0.0', '1.0.1-beta1'],
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ versions: ['1.0.0', '1.0.1-beta1'],
+ includePrereleases: true,
+ }).expect('1.0.1-beta1')
+
+ given({
+ versions: ['1.0.0', '1.0.1'],
+ includePrereleases: false,
+ }).expect('1.0.1')
+ given({
+ versions: ['1.0.1'],
+ includePrereleases: false,
+ }).expect('1.0.1')
+ given({
+ versions: ['1.0.0', '1.0.1-beta1'],
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ versions: ['1.0.0', '1.0.1-beta1'],
+ includePrereleases: true,
+ }).expect('1.0.1-beta1')
+
+ given({
+ versions: ['1.0.0+1', '1.0.1-beta1+1'],
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ versions: ['1.0.0+1', '1.0.1-beta1+1'],
+ includePrereleases: true,
+ }).expect('1.0.1-beta1')
+
+ given({ versions: [], includePrereleases: false }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ versions: [], includePrereleases: true }).expectError(
+ 'Not Found: package not found',
+ )
+ })
+})
diff --git a/services/feedz/feedz.tester.js b/services/feedz/feedz.tester.js
new file mode 100644
index 0000000000000..d240b8b237c8e
--- /dev/null
+++ b/services/feedz/feedz.tester.js
@@ -0,0 +1,98 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'feedz',
+ title: 'Feedz',
+ pathPrefix: '',
+})
+
+// The `shieldstests/public` repo is specifically made for these tests. It contains following packages:
+// - Shields.NoV1: 0.1.0
+// - Shields.TestPackage: 0.0.1, 0.1.0-pre, 1.0.0
+// - Shields.TestPreOnly: 0.1.0-pre
+// - Shields.MultiPage: 0.1.0-0.1.100 plus 1.0.0 but the response has multiple top-level `items`
+// - Shields.MultiPageNoItems: 0.0.0-0.0.256 plus 1.0.0 but the response has multiple top-level
+// `items` without `catalogEntries`
+// The source code of these packages is here: https://github.com/jakubfijalkowski/shields-test-packages
+
+// version
+t.create('version (valid)')
+ .get('/feedz/v/shieldstests/public/Shields.TestPackage.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v1.0.0',
+ color: 'blue',
+ })
+
+t.create('version (orange badge)')
+ .get('/feedz/v/shieldstests/public/Shields.NoV1.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v0.1.0',
+ color: 'orange',
+ })
+
+t.create('multi-page')
+ .get('/feedz/v/shieldstests/public/Shields.MultiPage.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v1.0.0',
+ color: 'blue',
+ })
+
+t.create('multi-page-no-items')
+ .get('/feedz/v/shieldstests/public/Shields.MultiPageNoItems.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v1.0.0',
+ color: 'blue',
+ })
+
+t.create('repository (not found)')
+ .get('/feedz/v/foo/bar/not-a-real-package.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
+
+t.create('version (not found)')
+ .get('/feedz/v/shieldstests/public/not-a-real-package.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
+
+t.create('non-existing repository')
+ .get('/feedz/v/shieldstests/does-not-exist/Shields.TestPackage.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
+
+// version (pre)
+t.create('version (pre) (valid)')
+ .get('/feedz/vpre/shieldstests/public/Shields.TestPackage.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v1.0.0',
+ color: 'blue',
+ })
+
+t.create('version (pre) (orange badge)')
+ .get('/feedz/vpre/shieldstests/public/Shields.NoV1.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v0.1.0',
+ color: 'orange',
+ })
+
+t.create('multi-page (pre)')
+ .get('/feedz/vpre/shieldstests/public/Shields.MultiPage.json')
+ .expectBadge({
+ label: 'feedz',
+ message: 'v1.0.0',
+ color: 'blue',
+ })
+
+t.create('repository (pre) (not found)')
+ .get('/feedz/vpre/foo/bar/not-a-real-package.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
+
+t.create('version (pre) (not found)')
+ .get('/feedz/vpre/shieldstests/public/not-a-real-package.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
+
+t.create('non-existing repository')
+ .get('/feedz/vpre/shieldstests/does-not-exist/Shields.TestPackage.json')
+ .expectBadge({ label: 'feedz', message: 'repository or package not found' })
diff --git a/services/flathub/flathub-downloads.service.js b/services/flathub/flathub-downloads.service.js
new file mode 100644
index 0000000000000..2e384e61ca776
--- /dev/null
+++ b/services/flathub/flathub-downloads.service.js
@@ -0,0 +1,35 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+
+const schema = Joi.object({
+ installs_total: Joi.number().integer().required(),
+}).required()
+
+export default class FlathubDownloads extends BaseJsonService {
+ static category = 'downloads'
+ static route = { base: 'flathub/downloads', pattern: ':packageName' }
+ static openApi = {
+ '/flathub/downloads/{packageName}': {
+ get: {
+ summary: 'Flathub Downloads',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'org.mozilla.firefox',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'installs' }
+
+ async handle({ packageName }) {
+ const data = await this._requestJson({
+ schema,
+ url: `https://flathub.org/api/v2/stats/${encodeURIComponent(
+ packageName,
+ )}`,
+ })
+ return renderDownloadsBadge({ downloads: data.installs_total })
+ }
+}
diff --git a/services/flathub/flathub-downloads.tester.js b/services/flathub/flathub-downloads.tester.js
new file mode 100644
index 0000000000000..deb763430dada
--- /dev/null
+++ b/services/flathub/flathub-downloads.tester.js
@@ -0,0 +1,14 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Flathub Downloads (valid)')
+ .get('/org.mozilla.firefox.json')
+ .expectBadge({
+ label: 'installs',
+ message: isMetric,
+ })
+
+t.create('Flathub Downloads (not found)')
+ .get('/not.a.package.json')
+ .expectBadge({ label: 'installs', message: 'not found' })
diff --git a/services/flathub/flathub-version.service.js b/services/flathub/flathub-version.service.js
new file mode 100644
index 0000000000000..1d4c3441e3413
--- /dev/null
+++ b/services/flathub/flathub-version.service.js
@@ -0,0 +1,43 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ releases: Joi.array().items(
+ Joi.object({
+ timestamp: Joi.string().required(),
+ version: Joi.string().required(),
+ }).required(),
+ ),
+}).required()
+
+export default class FlathubVersion extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'flathub/v', pattern: ':packageName' }
+ static openApi = {
+ '/flathub/v/{packageName}': {
+ get: {
+ summary: 'Flathub Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'org.mozilla.firefox',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'flathub' }
+
+ async handle({ packageName }) {
+ const { releases } = await this._requestJson({
+ schema,
+ url: `https://flathub.org/api/v2/appstream/${encodeURIComponent(packageName)}`,
+ })
+
+ const latestRelease = releases.sort(
+ (a, b) => parseInt(a['timestamp']) - parseInt(b['timestamp']),
+ )[releases.length - 1]
+
+ return renderVersionBadge({ version: latestRelease['version'] })
+ }
+}
diff --git a/services/flathub/flathub-version.tester.js b/services/flathub/flathub-version.tester.js
new file mode 100644
index 0000000000000..9d151af20aae6
--- /dev/null
+++ b/services/flathub/flathub-version.tester.js
@@ -0,0 +1,38 @@
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Flathub Version (valid)')
+ .get('/org.srb2.SRB2Kart-Saturn.json')
+ .expectBadge({
+ label: 'flathub',
+ message: isVPlusDottedVersionNClauses,
+ })
+
+t.create('Flathub Version (valid)')
+ .get('/org.mozilla.firefox.json')
+ .intercept(nock =>
+ nock('https://flathub.org')
+ .get('/api/v2/appstream/org.mozilla.firefox')
+ .reply(200, {
+ releases: [
+ {
+ timestamp: '1715769600',
+ version: '78.0.2',
+ },
+ {
+ timestamp: '1715769601',
+ version: '78.0.0',
+ },
+ {
+ timestamp: '1715769602',
+ version: '78.0.1',
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'flathub', message: 'v78.0.1' })
+
+t.create('Flathub Version (not found)')
+ .get('/not.a.package.json')
+ .expectBadge({ label: 'flathub', message: 'not found' })
diff --git a/services/freecodecamp/freecodecamp-points.service.js b/services/freecodecamp/freecodecamp-points.service.js
new file mode 100644
index 0000000000000..01fb117c6170a
--- /dev/null
+++ b/services/freecodecamp/freecodecamp-points.service.js
@@ -0,0 +1,81 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, InvalidResponse, pathParams } from '../index.js'
+
+/**
+ * Validates that the schema response is what we're expecting.
+ * The username pattern should match the requirements in the freeCodeCamp
+ * repository.
+ *
+ * @see https://github.com/freeCodeCamp/freeCodeCamp/blob/main/utils/validate.js
+ */
+const schema = Joi.object({
+ entities: Joi.object({
+ user: Joi.object()
+ .required()
+ .pattern(/^[a-zA-Z0-9\-_+]*$/, {
+ points: Joi.number().allow(null).required(),
+ }),
+ }).required(),
+}).required()
+
+/**
+ * This badge displays the total number of points a student has accumulated
+ * from completing challenges on freeCodeCamp.
+ */
+export default class FreeCodeCampPoints extends BaseJsonService {
+ static category = 'other'
+ static route = {
+ base: 'freecodecamp/points',
+ pattern: ':username',
+ }
+
+ static openApi = {
+ '/freecodecamp/points/{username}': {
+ get: {
+ summary: 'freeCodeCamp points',
+ parameters: pathParams({
+ name: 'username',
+ example: 'qapaloma',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'points', color: 'info' }
+
+ static render({ points }) {
+ return { message: metric(points) }
+ }
+
+ async fetch({ username }) {
+ return this._requestJson({
+ schema,
+ url: 'https://api.freecodecamp.org/users/get-public-profile',
+ options: {
+ searchParams: {
+ username,
+ },
+ },
+ httpErrors: { 404: 'profile not found' },
+ })
+ }
+
+ static transform(response, username) {
+ const { entities } = response
+
+ const { points } = entities.user[username]
+
+ if (points === null) {
+ throw new InvalidResponse({ prettyMessage: 'private' })
+ }
+
+ return points
+ }
+
+ async handle({ username }) {
+ const response = await this.fetch({ username })
+ const points = this.constructor.transform(response, username)
+ return this.constructor.render({ points })
+ }
+}
diff --git a/services/freecodecamp/freecodecamp-points.tester.js b/services/freecodecamp/freecodecamp-points.tester.js
new file mode 100644
index 0000000000000..93df9aff266c5
--- /dev/null
+++ b/services/freecodecamp/freecodecamp-points.tester.js
@@ -0,0 +1,15 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Total Points Valid')
+ .get('/qapaloma.json')
+ .expectBadge({ label: 'points', message: isMetric })
+
+t.create('Total Points Private')
+ .get('/set.json')
+ .expectBadge({ label: 'points', message: 'private' })
+
+t.create('Total Points Invalid')
+ .get('/invalid@username.json')
+ .expectBadge({ label: 'points', message: 'profile not found' })
diff --git a/services/galaxytoolshed/galaxytoolshed-activity.service.js b/services/galaxytoolshed/galaxytoolshed-activity.service.js
new file mode 100644
index 0000000000000..c0f7cc087bbc0
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-activity.service.js
@@ -0,0 +1,42 @@
+import { pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import BaseGalaxyToolshedService from './galaxytoolshed-base.js'
+
+export default class GalaxyToolshedCreatedDate extends BaseGalaxyToolshedService {
+ static category = 'activity'
+ static route = {
+ base: 'galaxytoolshed/created-date',
+ pattern: ':repository/:owner',
+ }
+
+ static openApi = {
+ '/galaxytoolshed/created-date/{repository}/{owner}': {
+ get: {
+ summary: 'Galaxy Toolshed - Created Date',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'sra_tools',
+ },
+ {
+ name: 'owner',
+ example: 'iuc',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'created date',
+ }
+
+ async handle({ repository, owner }) {
+ const response = await this.fetchLastOrderedInstallableRevisionsSchema({
+ repository,
+ owner,
+ })
+ const { create_time: date } = response[0]
+ return renderDateBadge(date, true)
+ }
+}
diff --git a/services/galaxytoolshed/galaxytoolshed-activity.tester.js b/services/galaxytoolshed/galaxytoolshed-activity.tester.js
new file mode 100644
index 0000000000000..94c80a684d830
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-activity.tester.js
@@ -0,0 +1,23 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Created Date')
+ .get('/sra_tools/iuc.json')
+ .expectBadge({ label: 'created date', message: isFormattedDate })
+
+t.create('Created Date - repository not found')
+ .get('/sra_tool/iuc.json')
+ .expectBadge({ label: 'created date', message: 'not found' })
+
+t.create('Created Date - owner not found')
+ .get('/sra_tools/iu.json')
+ .expectBadge({ label: 'created date', message: 'not found' })
+
+t.create('Created Date - changesetRevision not found')
+ .get('/bioqc/badilla.json')
+ .expectBadge({
+ label: 'created date',
+ message: 'changesetRevision not found',
+ })
diff --git a/services/galaxytoolshed/galaxytoolshed-base.js b/services/galaxytoolshed/galaxytoolshed-base.js
new file mode 100644
index 0000000000000..145f98b5d87d6
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-base.js
@@ -0,0 +1,55 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { NotFound, BaseJsonService } from '../index.js'
+
+const orderedInstallableRevisionsSchema = Joi.array()
+ .items(Joi.string())
+ .required()
+
+const repositoryRevisionInstallInfoSchema = Joi.array()
+ .ordered(
+ Joi.object({
+ create_time: Joi.date().required(),
+ times_downloaded: nonNegativeInteger,
+ }).required(),
+ )
+ .items(Joi.any())
+
+export default class BaseGalaxyToolshedService extends BaseJsonService {
+ static defaultBadgeData = { label: 'galaxytoolshed' }
+ static baseUrl = 'https://toolshed.g2.bx.psu.edu'
+
+ async fetchOrderedInstallableRevisionsSchema({ repository, owner }) {
+ return this._requestJson({
+ schema: orderedInstallableRevisionsSchema,
+ url: `${this.constructor.baseUrl}/api/repositories/get_ordered_installable_revisions?name=${repository}&owner=${owner}`,
+ })
+ }
+
+ async fetchRepositoryRevisionInstallInfoSchema({
+ repository,
+ owner,
+ changesetRevision,
+ }) {
+ return this._requestJson({
+ schema: repositoryRevisionInstallInfoSchema,
+ url: `${this.constructor.baseUrl}/api/repositories/get_repository_revision_install_info?name=${repository}&owner=${owner}&changeset_revision=${changesetRevision}`,
+ })
+ }
+
+ async fetchLastOrderedInstallableRevisionsSchema({ repository, owner }) {
+ const changesetRevisions =
+ await this.fetchOrderedInstallableRevisionsSchema({
+ repository,
+ owner,
+ })
+ if (!Array.isArray(changesetRevisions) || !changesetRevisions.length) {
+ throw new NotFound({ prettyMessage: 'changesetRevision not found' })
+ }
+ return this.fetchRepositoryRevisionInstallInfoSchema({
+ repository,
+ owner,
+ changesetRevision: changesetRevisions[0],
+ })
+ }
+}
diff --git a/services/galaxytoolshed/galaxytoolshed-downloads.service.js b/services/galaxytoolshed/galaxytoolshed-downloads.service.js
new file mode 100644
index 0000000000000..0267661147ed1
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-downloads.service.js
@@ -0,0 +1,42 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import BaseGalaxyToolshedService from './galaxytoolshed-base.js'
+
+export default class GalaxyToolshedDownloads extends BaseGalaxyToolshedService {
+ static category = 'downloads'
+ static route = {
+ base: 'galaxytoolshed/downloads',
+ pattern: ':repository/:owner',
+ }
+
+ static openApi = {
+ '/galaxytoolshed/downloads/{repository}/{owner}': {
+ get: {
+ summary: 'Galaxy Toolshed - Downloads',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'sra_tools',
+ },
+ {
+ name: 'owner',
+ example: 'iuc',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'downloads',
+ }
+
+ async handle({ repository, owner }) {
+ const response = await this.fetchLastOrderedInstallableRevisionsSchema({
+ repository,
+ owner,
+ })
+ const { times_downloaded: downloads } = response[0]
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/galaxytoolshed/galaxytoolshed-downloads.tester.js b/services/galaxytoolshed/galaxytoolshed-downloads.tester.js
new file mode 100644
index 0000000000000..2695e297b781c
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-downloads.tester.js
@@ -0,0 +1,28 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('downloads - raw').get('/sra_tools/iuc.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('downloads - repository not found')
+ .get('/sra_tool/iuc.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'not found',
+ })
+
+t.create('downloads - owner not found').get('/sra_tools/iu.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
+
+t.create('downloads - changesetRevision not found')
+ .get('/bioqc/badilla.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'changesetRevision not found',
+ })
diff --git a/services/galaxytoolshed/galaxytoolshed-version.service.js b/services/galaxytoolshed/galaxytoolshed-version.service.js
new file mode 100644
index 0000000000000..90f9b88cde416
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-version.service.js
@@ -0,0 +1,107 @@
+import { NotFound, pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import GalaxyToolshedService from './galaxytoolshed-base.js'
+
+export class GalaxyToolshedVersion extends GalaxyToolshedService {
+ static category = 'version'
+ static route = {
+ base: 'galaxytoolshed/v',
+ pattern: ':repository/:owner/:tool?/:requirement?',
+ }
+
+ static openApi = {
+ '/galaxytoolshed/v/{repository}/{owner}': {
+ get: {
+ summary: 'Galaxy Toolshed - Repository Version',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'sra_tools',
+ },
+ {
+ name: 'owner',
+ example: 'iuc',
+ },
+ ),
+ },
+ },
+ '/galaxytoolshed/v/{repository}/{owner}/{tool}': {
+ get: {
+ summary: 'Galaxy Toolshed - Tool Version',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'sra_tools',
+ },
+ {
+ name: 'owner',
+ example: 'iuc',
+ },
+ {
+ name: 'tool',
+ example: 'fastq_dump',
+ },
+ ),
+ },
+ },
+ '/galaxytoolshed/v/{repository}/{owner}/{tool}/{requirement}': {
+ get: {
+ summary: 'Galaxy Toolshed - Tool Requirement Version',
+ parameters: pathParams(
+ {
+ name: 'repository',
+ example: 'sra_tools',
+ },
+ {
+ name: 'owner',
+ example: 'iuc',
+ },
+ {
+ name: 'tool',
+ example: 'fastq_dump',
+ },
+ {
+ name: 'requirement',
+ example: 'perl',
+ },
+ ),
+ },
+ },
+ }
+
+ static transform({ response, tool, requirement }) {
+ if (tool !== undefined) {
+ const dataTool = response[1].valid_tools.find(x => x.id === tool)
+ if (dataTool === undefined) {
+ throw new NotFound({ prettyMessage: 'tool not found' })
+ }
+ // Requirement version
+ if (requirement !== undefined) {
+ const dataRequirement = dataTool.requirements.find(
+ x => x.name === requirement,
+ )
+ if (dataRequirement === undefined) {
+ throw new NotFound({ prettyMessage: 'requirement not found' })
+ }
+ return dataRequirement.version
+ }
+ // Tool version
+ return dataTool.version
+ }
+ // Repository version
+ return response[1].changeset_revision
+ }
+
+ async handle({ repository, owner, tool, requirement }) {
+ const response = await this.fetchLastOrderedInstallableRevisionsSchema({
+ repository,
+ owner,
+ })
+ const version = this.constructor.transform({
+ response,
+ tool,
+ requirement,
+ })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/galaxytoolshed/galaxytoolshed-version.tester.js b/services/galaxytoolshed/galaxytoolshed-version.tester.js
new file mode 100644
index 0000000000000..bc14a6749d0e2
--- /dev/null
+++ b/services/galaxytoolshed/galaxytoolshed-version.tester.js
@@ -0,0 +1,51 @@
+import { withRegex, isVPlusTripleDottedVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('version - repository')
+ .get('/sra_tools/iuc.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: withRegex(/^([\w\d]+)$/),
+ })
+t.create('version - tool').get('/sra_tools/iuc/fastq_dump.json').expectBadge({
+ label: 'galaxytoolshed',
+ message: isVPlusTripleDottedVersion,
+})
+t.create('version - requirement')
+ .get('/sra_tools/iuc/fastq_dump/perl.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: isVPlusTripleDottedVersion,
+ })
+
+// Not found
+t.create('version - changesetRevision not found')
+ .get('/bioqc/badilla.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: 'changesetRevision not found',
+ })
+t.create('version - repository not found')
+ .get('/sra_too/iuc.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: 'not found',
+ })
+t.create('version - owner not found').get('/sra_tool/iu.json').expectBadge({
+ label: 'galaxytoolshed',
+ message: 'not found',
+})
+t.create('version - tool not found')
+ .get('/sra_tools/iuc/fastq_dum.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: 'tool not found',
+ })
+t.create('version - requirement not found')
+ .get('/sra_tools/iuc/fastq_dump/per.json')
+ .expectBadge({
+ label: 'galaxytoolshed',
+ message: 'requirement not found',
+ })
diff --git a/services/gem/gem-downloads.service.js b/services/gem/gem-downloads.service.js
index ad4bd90f347b8..15d911218bd90 100644
--- a/services/gem/gem-downloads.service.js
+++ b/services/gem/gem-downloads.service.js
@@ -1,14 +1,15 @@
-'use strict'
-
-const semver = require('semver')
-const Joi = require('@hapi/joi')
-const { downloadCount } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { latest: latestVersion } = require('../version')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, InvalidParameter, InvalidResponse } = require('..')
-
-const keywords = ['ruby']
+import semver from 'semver'
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { latest as latestVersion } from '../version.js'
+import { nonNegativeInteger } from '../validators.js'
+import {
+ BaseJsonService,
+ InvalidParameter,
+ InvalidResponse,
+ pathParams,
+} from '../index.js'
+import { description } from './gem-helpers.js'
const gemSchema = Joi.object({
downloads: nonNegativeInteger,
@@ -21,100 +22,65 @@ const versionSchema = Joi.array()
prerelease: Joi.boolean().required(),
number: Joi.string().required(),
downloads_count: nonNegativeInteger,
- })
+ }),
)
.min(1)
.required()
-module.exports = class GemDownloads extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'gem',
- pattern: ':variant(dt|dtv|dv)/:gem/:version?',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Gem',
- pattern: 'dv/:gem/:version',
- namedParams: {
- gem: 'rails',
- version: 'stable',
- },
- staticPreview: this.render({
- variant: 'dv',
- version: 'stable',
- downloads: 70000,
- }),
- keywords,
- },
- {
- title: 'Gem',
- pattern: 'dv/:gem/:version',
- namedParams: {
- gem: 'rails',
- version: '4.1.0',
- },
- staticPreview: this.render({
- variant: 'dv',
- version: '4.1.0',
- downloads: 50000,
+export default class GemDownloads extends BaseJsonService {
+ static category = 'downloads'
+ static route = { base: 'gem', pattern: ':variant(dt|dtv|dv)/:gem/:version?' }
+ static openApi = {
+ '/gem/dt/{gem}': {
+ get: {
+ summary: 'Gem Total Downloads',
+ description,
+ parameters: pathParams({
+ name: 'gem',
+ example: 'rails',
}),
- keywords,
},
- {
- title: 'Gem',
- pattern: 'dtv/:gem',
- namedParams: { gem: 'rails' },
- staticPreview: this.render({
- variant: 'dtv',
- downloads: 70000,
+ },
+ '/gem/dtv/{gem}': {
+ get: {
+ summary: 'Gem Downloads (for latest version)',
+ description,
+ parameters: pathParams({
+ name: 'gem',
+ example: 'rails',
}),
- keywords,
},
- {
- title: 'Gem',
- pattern: 'dt/:gem',
- namedParams: { gem: 'rails' },
- staticPreview: this.render({
- variant: 'dt',
- downloads: 900000,
- }),
- keywords,
+ },
+ '/gem/dv/{gem}/{version}': {
+ get: {
+ summary: 'Gem Downloads (for specified version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'gem',
+ example: 'rails',
+ },
+ {
+ name: 'version',
+ example: '4.1.0',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static defaultBadgeData = { label: 'downloads' }
static render({ variant, version, downloads }) {
- let label
- if (version) {
- label = `downloads@${version}`
- } else if (variant === 'dtv') {
- label = 'downloads@latest'
- }
-
- return {
- label,
- message: metric(downloads),
- color: downloadCount(downloads),
- }
+ version = !version && variant === 'dtv' ? 'latest' : version
+ return renderDownloadsBadge({ downloads, version })
}
async fetchDownloadCountForVersion({ gem, version }) {
const json = await this._requestJson({
url: `https://rubygems.org/api/v1/versions/${gem}.json`,
schema: versionSchema,
- errorMessages: {
+ httpErrors: {
404: 'gem not found',
},
})
@@ -122,7 +88,9 @@ module.exports = class GemDownloads extends BaseJsonService {
let wantedVersion
if (version === 'stable') {
wantedVersion = latestVersion(
- json.filter(({ prerelease }) => !prerelease).map(({ number }) => number)
+ json
+ .filter(({ prerelease }) => !prerelease)
+ .map(({ number }) => number),
)
} else {
wantedVersion = version
@@ -139,16 +107,14 @@ module.exports = class GemDownloads extends BaseJsonService {
}
async fetchDownloadCountForGem({ gem }) {
- const {
- downloads: totalDownloads,
- version_downloads: versionDownloads,
- } = await this._requestJson({
- url: `https://rubygems.org/api/v1/gems/${gem}.json`,
- schema: gemSchema,
- errorMessages: {
- 404: 'gem not found',
- },
- })
+ const { downloads: totalDownloads, version_downloads: versionDownloads } =
+ await this._requestJson({
+ url: `https://rubygems.org/api/v1/gems/${gem}.json`,
+ schema: gemSchema,
+ httpErrors: {
+ 404: 'gem not found',
+ },
+ })
return { totalDownloads, versionDownloads }
}
@@ -167,10 +133,8 @@ module.exports = class GemDownloads extends BaseJsonService {
}
downloads = await this.fetchDownloadCountForVersion({ gem, version })
} else {
- const {
- totalDownloads,
- versionDownloads,
- } = await this.fetchDownloadCountForGem({ gem, variant })
+ const { totalDownloads, versionDownloads } =
+ await this.fetchDownloadCountForGem({ gem, variant })
downloads = variant === 'dtv' ? versionDownloads : totalDownloads
}
return this.constructor.render({ variant, version, downloads })
diff --git a/services/gem/gem-downloads.tester.js b/services/gem/gem-downloads.tester.js
index c42912ecaaecd..cd709bb4bfe1c 100644
--- a/services/gem/gem-downloads.tester.js
+++ b/services/gem/gem-downloads.tester.js
@@ -1,14 +1,11 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('total downloads (valid)')
- .get('/dt/rails.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('total downloads (valid)').get('/dt/rails.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('total downloads (not found)')
.get('/dt/not-a-package.json')
diff --git a/services/gem/gem-helpers.js b/services/gem/gem-helpers.js
new file mode 100644
index 0000000000000..13c475ea0bcc3
--- /dev/null
+++ b/services/gem/gem-helpers.js
@@ -0,0 +1,28 @@
+import { valid, maxSatisfying, prerelease } from '@renovatebot/ruby-semver'
+
+const description =
+ '[Ruby Gems](https://rubygems.org/) is a registry for ruby libraries'
+
+function latest(versions) {
+ // latest Ruby Gems version, including pre-releases
+ return maxSatisfying(versions, '>0')
+}
+
+function versionColor(version) {
+ if (!valid(version)) {
+ return 'lightgrey'
+ }
+
+ version = `${version}`
+ let first = version[0]
+ if (first === 'v') {
+ first = version[1]
+ }
+
+ if (first === '0' || prerelease(version)) {
+ return 'orange'
+ }
+ return 'blue'
+}
+
+export { description, latest, versionColor }
diff --git a/services/gem/gem-helpers.spec.js b/services/gem/gem-helpers.spec.js
new file mode 100644
index 0000000000000..90d2a1761376f
--- /dev/null
+++ b/services/gem/gem-helpers.spec.js
@@ -0,0 +1,17 @@
+import { test, given } from 'sazerac'
+import { latest, versionColor } from './gem-helpers.js'
+
+describe('Gem helpers', function () {
+ test(latest, () => {
+ given(['2.0.0', '2.0.0.beta1']).expect('2.0.0')
+ given(['2.0.0.beta1', '1.9.0']).expect('2.0.0.beta1')
+ given(['0.0.1', '0.0.2']).expect('0.0.2')
+ })
+
+ test(versionColor, () => {
+ given('1.9.0').expect('blue')
+ given('2.0.0.beta1').expect('orange')
+ given('0.0.1').expect('orange')
+ given('v1').expect('lightgrey')
+ })
+})
diff --git a/services/gem/gem-owner.service.js b/services/gem/gem-owner.service.js
index 11787c16552db..b4b86577079ca 100644
--- a/services/gem/gem-owner.service.js
+++ b/services/gem/gem-owner.service.js
@@ -1,41 +1,32 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { description } from './gem-helpers.js'
const ownerSchema = Joi.array().required()
-module.exports = class GemOwner extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'gem/u',
- pattern: ':user',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Gems',
- namedParams: { user: 'raphink' },
- staticPreview: this.render({ count: 34 }),
- keywords: ['ruby'],
+export default class GemOwner extends BaseJsonService {
+ static category = 'other'
+ static route = { base: 'gem/u', pattern: ':user' }
+ static openApi = {
+ '/gem/u/{user}': {
+ get: {
+ summary: 'Gem Owner',
+ description,
+ parameters: pathParams({
+ name: 'user',
+ example: 'raphink',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'gems' }
- }
+ static defaultBadgeData = { label: 'gems' }
static render({ count }) {
return {
- message: count,
+ message: metric(count),
color: floorCountColor(count, 10, 50, 100),
}
}
diff --git a/services/gem/gem-owner.tester.js b/services/gem/gem-owner.tester.js
index 4513dce246225..9156752cb3553 100644
--- a/services/gem/gem-owner.tester.js
+++ b/services/gem/gem-owner.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('users (valid)')
.get('/raphink.json')
diff --git a/services/gem/gem-rank.service.js b/services/gem/gem-rank.service.js
index e63470d4137cc..2377811e65924 100644
--- a/services/gem/gem-rank.service.js
+++ b/services/gem/gem-rank.service.js
@@ -1,73 +1,51 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { floorCount } = require('../color-formatters')
-const { ordinalNumber } = require('../text-formatters')
-const { BaseJsonService, InvalidResponse } = require('..')
-
-const keywords = ['ruby']
+import Joi from 'joi'
+import { floorCount } from '../color-formatters.js'
+import { ordinalNumber } from '../text-formatters.js'
+import { BaseJsonService, InvalidResponse, pathParams } from '../index.js'
+import { description } from './gem-helpers.js'
const totalSchema = Joi.array()
.items(
Joi.object({
- total_ranking: Joi.number()
- .integer()
- .min(0)
- .allow(null),
- })
+ total_ranking: Joi.number().integer().min(0).allow(null),
+ }),
)
.min(1)
.required()
const dailySchema = Joi.array()
.items(
Joi.object({
- daily_ranking: Joi.number()
- .integer()
- .min(0)
- .allow(null),
- })
+ daily_ranking: Joi.number().integer().min(0).allow(null),
+ }),
)
.min(1)
.required()
-module.exports = class GemRank extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'gem',
- pattern: ':period(rt|rd)/:gem',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Gem download rank',
- pattern: 'rt/:gem',
- namedParams: {
- gem: 'puppet',
- },
- staticPreview: this.render({ period: 'rt', rank: 332 }),
- keywords,
- },
- {
- title: 'Gem download rank (daily)',
- pattern: 'rd/:gem',
- namedParams: {
- gem: 'facter',
- },
- staticPreview: this.render({ period: 'rd', rank: 656 }),
- keywords,
+export default class GemRank extends BaseJsonService {
+ static category = 'downloads'
+ static route = { base: 'gem', pattern: ':period(rt|rd)/:gem' }
+ static openApi = {
+ '/gem/{period}/{gem}': {
+ get: {
+ summary: 'Gem download rank',
+ description,
+ parameters: pathParams(
+ {
+ name: 'period',
+ example: 'rt',
+ description: 'total or daily ranking',
+ schema: { type: 'string', enum: this.getEnum('period') },
+ },
+ {
+ name: 'gem',
+ example: 'puppet',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'rank' }
- }
+ static defaultBadgeData = { label: 'rank' }
static render({ period, rank }) {
const count = Math.floor(100000 / rank)
diff --git a/services/gem/gem-rank.tester.js b/services/gem/gem-rank.tester.js
index 5a8bd5b4fc8a0..ca70f15e6a79c 100644
--- a/services/gem/gem-rank.tester.js
+++ b/services/gem/gem-rank.tester.js
@@ -1,26 +1,16 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isOrdinalNumber, isOrdinalNumberDaily } from '../test-validators.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('total rank (valid)').get('/rt/rspec-puppet-facts.json').expectBadge({
+ label: 'rank',
+ message: isOrdinalNumber,
+})
-const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/)
-const isOrdinalNumberDaily = Joi.string().regex(
- /^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/
-)
-
-t.create('total rank (valid)')
- .get('/rt/rspec-puppet-facts.json')
- .expectBadge({
- label: 'rank',
- message: isOrdinalNumber,
- })
-
-t.create('daily rank (valid)')
- .get('/rd/rails.json')
- .expectBadge({
- label: 'rank',
- message: isOrdinalNumberDaily,
- })
+t.create('daily rank (valid)').get('/rd/rails.json').expectBadge({
+ label: 'rank',
+ message: isOrdinalNumberDaily,
+})
t.create('rank (not found)')
.get('/rt/not-a-package.json')
@@ -36,6 +26,6 @@ t.create('rank is null')
date: '2019-01-06',
daily_ranking: null,
},
- ])
+ ]),
)
.expectBadge({ label: 'rank', message: 'invalid rank' })
diff --git a/services/gem/gem-version.service.js b/services/gem/gem-version.service.js
index abbdfecce41cd..836edce404b07 100644
--- a/services/gem/gem-version.service.js
+++ b/services/gem/gem-version.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { description, latest, versionColor } from './gem-helpers.js'
const schema = Joi.object({
// In most cases `version` will be a SemVer but the registry doesn't
@@ -10,35 +9,46 @@ const schema = Joi.object({
version: Joi.string().required(),
}).required()
-module.exports = class GemVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
+const versionSchema = Joi.array()
+ .items(
+ Joi.object({
+ number: Joi.string().required(),
+ }),
+ )
+ .min(1)
+ .required()
- static get route() {
- return {
- base: 'gem/v',
- pattern: ':gem',
- }
- }
+const queryParamSchema = Joi.object({
+ include_prereleases: Joi.equal(''),
+}).required()
- static get examples() {
- return [
- {
- title: 'Gem',
- namedParams: { gem: 'formatador' },
- staticPreview: this.render({ version: '2.1.0' }),
- keywords: ['ruby'],
+export default class GemVersion extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'gem/v', pattern: ':gem', queryParamSchema }
+ static openApi = {
+ '/gem/v/{gem}': {
+ get: {
+ summary: 'Gem Version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'gem',
+ example: 'formatador',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'gem' }
- }
+ static defaultBadgeData = { label: 'gem' }
static render({ version }) {
- return renderVersionBadge({ version })
+ return renderVersionBadge({ version, versionFormatter: versionColor })
}
async fetch({ gem }) {
@@ -48,8 +58,21 @@ module.exports = class GemVersion extends BaseJsonService {
})
}
- async handle({ gem }) {
- const { version } = await this.fetch({ gem })
- return this.constructor.render({ version })
+ async fetchLatest({ gem }) {
+ return this._requestJson({
+ schema: versionSchema,
+ url: `https://rubygems.org/api/v1/versions/${gem}.json`,
+ })
+ }
+
+ async handle({ gem }, queryParams) {
+ if (queryParams && typeof queryParams.include_prereleases !== 'undefined') {
+ const data = await this.fetchLatest({ gem })
+ const versions = data.map(version => version.number)
+ return this.constructor.render({ version: latest(versions) })
+ } else {
+ const { version } = await this.fetch({ gem })
+ return this.constructor.render({ version })
+ }
}
}
diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js
index 88caac14f49c4..fbc75abcb3129 100644
--- a/services/gem/gem-version.tester.js
+++ b/services/gem/gem-version.tester.js
@@ -1,15 +1,31 @@
-'use strict'
+import {
+ isVPlusDottedVersionAtLeastOne,
+ withRegex,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('version (valid)').get('/formatador.json').expectBadge({
+ label: 'gem',
+ message: isVPlusDottedVersionAtLeastOne,
+})
-t.create('version (valid)')
- .get('/formatador.json')
+t.create('version (not found)')
+ .get('/not-a-package.json')
+ .expectBadge({ label: 'gem', message: 'not found' })
+
+// this is the same as isVPlusDottedVersionNClausesWithOptionalSuffix from test-validators.js
+// except that it also accepts regexes like 5.0.0.rc5 - the . before the rc5 is not accepted in the original
+const isVPlusDottedVersionNClausesWithOptionalSuffix = withRegex(
+ /^v\d+(\.\d+)*([-+~.].*)?$/,
+)
+t.create('version including prereleases (valid)')
+ .get('/flame.json?include_prereleases')
.expectBadge({
label: 'gem',
- message: isVPlusDottedVersionAtLeastOne,
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
-t.create('version (not found)')
- .get('/not-a-package.json')
+t.create('version including prereleases (not found)')
+ .get('/not-a-package.json?include_prereleases')
.expectBadge({ label: 'gem', message: 'not found' })
diff --git a/services/gemnasium/gemnasium.service.js b/services/gemnasium/gemnasium.service.js
deleted file mode 100644
index b3a76ac147a96..0000000000000
--- a/services/gemnasium/gemnasium.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'dependencies',
- route: {
- base: 'gemnasium',
- pattern: ':various+',
- },
- label: 'gemnasium',
- dateAdded: new Date('2018-05-15'),
-})
diff --git a/services/gemnasium/gemnasium.tester.js b/services/gemnasium/gemnasium.tester.js
deleted file mode 100644
index 152a1ed98577c..0000000000000
--- a/services/gemnasium/gemnasium.tester.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'gemnasium',
- title: 'gemnasium',
-}))
-
-t.create('no longer available (previously dependencies)')
- .get('/mathiasbynens/he.json')
- .expectBadge({
- label: 'gemnasium',
- message: 'no longer available',
- })
diff --git a/services/gerrit/gerrit.service.js b/services/gerrit/gerrit.service.js
index 078df046ce2f5..03904768a1b4b 100644
--- a/services/gerrit/gerrit.service.js
+++ b/services/gerrit/gerrit.service.js
@@ -1,49 +1,38 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
const queryParamSchema = Joi.object({
- baseUrl: optionalUrl.required(),
+ baseUrl: url,
}).required()
const schema = Joi.object({
status: Joi.equal('NEW', 'MERGED', 'ABANDONED').required(),
}).required()
-module.exports = class Gerrit extends BaseJsonService {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'gerrit',
- pattern: ':changeId',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Gerrit change status',
- namedParams: {
- changeId: '1011478',
- },
- queryParams: { baseUrl: 'https://android-review.googlesource.com' },
- staticPreview: this.render({
- changeId: 1011478,
- status: 'MERGED',
- }),
+export default class Gerrit extends BaseJsonService {
+ static category = 'issue-tracking'
+ static route = { base: 'gerrit', pattern: ':changeId', queryParamSchema }
+ static openApi = {
+ '/gerrit/{changeId}': {
+ get: {
+ summary: 'Gerrit change status',
+ parameters: [
+ pathParam({
+ name: 'changeId',
+ example: '1011478',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://android-review.googlesource.com',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'gerrit' }
- }
+ static defaultBadgeData = { label: 'gerrit' }
static getColor({ displayStatus }) {
if (displayStatus === 'new') {
@@ -80,7 +69,7 @@ module.exports = class Gerrit extends BaseJsonService {
return this._requestJson({
schema,
url: `${baseUrl}/changes/${changeId}`,
- errorMessages: {
+ httpErrors: {
404: 'change not found',
},
})
diff --git a/services/gerrit/gerrit.tester.js b/services/gerrit/gerrit.tester.js
index b4468af421a72..3985782289ddc 100644
--- a/services/gerrit/gerrit.tester.js
+++ b/services/gerrit/gerrit.tester.js
@@ -1,12 +1,12 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Change open since December 2010, hopefully won't get merged or abandoned anytime soon.
+// Change open since September 2017, hopefully won't get merged or abandoned anytime soon.
+// https://android-review.googlesource.com/c/platform/bootable/recovery/+/494609
t.create('Gerrit new change')
- .get('/2013.json?baseUrl=https://git.eclipse.org/r')
+ .get('/494609.json?baseUrl=https://android-review.googlesource.com')
.expectBadge({
- label: 'change 2013',
+ label: 'change 494609',
message: 'new',
color: '#2cbe4e',
})
diff --git a/services/gitea/gitea-base.js b/services/gitea/gitea-base.js
new file mode 100644
index 0000000000000..2a14ba846ef01
--- /dev/null
+++ b/services/gitea/gitea-base.js
@@ -0,0 +1,19 @@
+import { BaseJsonService } from '../index.js'
+
+export default class GiteaBase extends BaseJsonService {
+ static auth = {
+ passKey: 'gitea_token',
+ serviceKey: 'gitea',
+ }
+
+ async fetch({ url, options, schema, httpErrors }) {
+ return this._requestJson(
+ this.authHelper.withBearerAuthHeader({
+ schema,
+ url,
+ options,
+ httpErrors,
+ }),
+ )
+ }
+}
diff --git a/services/gitea/gitea-base.spec.js b/services/gitea/gitea-base.spec.js
new file mode 100644
index 0000000000000..0323801ca5888
--- /dev/null
+++ b/services/gitea/gitea-base.spec.js
@@ -0,0 +1,48 @@
+import Joi from 'joi'
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import GiteaBase from './gitea-base.js'
+
+class DummyGiteaService extends GiteaBase {
+ static route = { base: 'fake-base' }
+
+ async handle() {
+ const data = await this.fetch({
+ schema: Joi.any(),
+ url: 'https://gitea.com/api/v1/repos/CanisHelix/shields-badge-test/releases',
+ })
+ return { message: data.message }
+ }
+}
+
+describe('GiteaBase', function () {
+ describe('auth', function () {
+ cleanUpNockAfterEach()
+
+ const config = {
+ public: {
+ services: {
+ gitea: {
+ authorizedOrigins: ['https://gitea.com'],
+ },
+ },
+ },
+ private: {
+ gitea_token: 'fake-key',
+ },
+ }
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://gitea.com')
+ .get('/api/v1/repos/CanisHelix/shields-badge-test/releases')
+ .matchHeader('Authorization', 'Bearer fake-key')
+ .reply(200, { message: 'fake message' })
+ expect(
+ await DummyGiteaService.invoke(defaultContext, config, {}),
+ ).to.not.have.property('isError')
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/gitea/gitea-common-fetch.js b/services/gitea/gitea-common-fetch.js
new file mode 100644
index 0000000000000..84bc11a94bde4
--- /dev/null
+++ b/services/gitea/gitea-common-fetch.js
@@ -0,0 +1,14 @@
+async function fetchIssue(
+ serviceInstance,
+ { user, repo, baseUrl, options, httpErrors },
+) {
+ return serviceInstance._request(
+ serviceInstance.authHelper.withBearerAuthHeader({
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}/issues`,
+ options,
+ httpErrors,
+ }),
+ )
+}
+
+export { fetchIssue }
diff --git a/services/gitea/gitea-forks.service.js b/services/gitea/gitea-forks.service.js
new file mode 100644
index 0000000000000..ac830c85383dc
--- /dev/null
+++ b/services/gitea/gitea-forks.service.js
@@ -0,0 +1,76 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import GiteaBase from './gitea-base.js'
+import { description, httpErrorsFor } from './gitea-helper.js'
+
+const schema = Joi.object({
+ forks_count: nonNegativeInteger,
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaForks extends GiteaBase {
+ static category = 'social'
+
+ static route = {
+ base: 'gitea/forks',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/forks/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Forks',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'forks', namedLogo: 'gitea' }
+
+ static render({ baseUrl, user, repo, forkCount }) {
+ return {
+ message: metric(forkCount),
+ style: 'social',
+ color: 'blue',
+ link: [`${baseUrl}/${user}/${repo}`, `${baseUrl}/${user}/${repo}/forks`],
+ }
+ }
+
+ async fetch({ user, repo, baseUrl }) {
+ // https://gitea.com/api/swagger#/repository
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}`,
+ httpErrors: httpErrorsFor(),
+ })
+ }
+
+ async handle({ user, repo }, { gitea_url: baseUrl = 'https://gitea.com' }) {
+ const { forks_count: forkCount } = await this.fetch({
+ user,
+ repo,
+ baseUrl,
+ })
+ return this.constructor.render({ baseUrl, user, repo, forkCount })
+ }
+}
diff --git a/services/gitea/gitea-forks.tester.js b/services/gitea/gitea-forks.tester.js
new file mode 100644
index 0000000000000..1795572231fe2
--- /dev/null
+++ b/services/gitea/gitea-forks.tester.js
@@ -0,0 +1,32 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Forks')
+ .get('/gitea/tea.json')
+ .expectBadge({
+ label: 'forks',
+ message: isMetric,
+ color: 'blue',
+ link: ['https://gitea.com/gitea/tea', 'https://gitea.com/gitea/tea/forks'],
+ })
+
+t.create('Forks (self-managed)')
+ .get('/Codeberg/forgejo.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'forks',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://codeberg.org/Codeberg/forgejo',
+ 'https://codeberg.org/Codeberg/forgejo/forks',
+ ],
+ })
+
+t.create('Forks (project not found)')
+ .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'forks',
+ message: 'user or repo not found',
+ })
diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js
new file mode 100644
index 0000000000000..3d2e575d5ccae
--- /dev/null
+++ b/services/gitea/gitea-helper.js
@@ -0,0 +1,35 @@
+import { metric } from '../text-formatters.js'
+
+const description = `
+By default this badge looks for repositories on [gitea.com](https://gitea.com).
+To specify another instance like [codeberg](https://codeberg.org/), [forgejo](https://forgejo.org/) or a self-hosted instance, use the \`gitea_url\` query param.
+`
+
+function httpErrorsFor(notFoundMessage = 'user or repo not found') {
+ return {
+ 403: 'private repo',
+ 404: notFoundMessage,
+ }
+}
+
+function renderIssue({ variant, labels, defaultBadgeData, count }) {
+ const state = variant.split('-')[0]
+ const raw = variant.endsWith('-raw')
+ const isMultiLabel = labels && labels.includes(',')
+ const labelText = labels ? `${isMultiLabel ? `${labels}` : labels} ` : ''
+
+ let labelPrefix = ''
+ let messageSuffix = ''
+ if (raw) {
+ labelPrefix = `${state} `
+ } else {
+ messageSuffix = state
+ }
+ return {
+ label: `${labelPrefix}${labelText}${defaultBadgeData.label}`,
+ message: `${metric(count)}${messageSuffix ? ' ' : ''}${messageSuffix}`,
+ color: count > 0 ? 'yellow' : 'brightgreen',
+ }
+}
+
+export { description, httpErrorsFor, renderIssue }
diff --git a/services/gitea/gitea-issues.service.js b/services/gitea/gitea-issues.service.js
new file mode 100644
index 0000000000000..cac75aaea869b
--- /dev/null
+++ b/services/gitea/gitea-issues.service.js
@@ -0,0 +1,96 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { fetchIssue } from './gitea-common-fetch.js'
+import { description, httpErrorsFor, renderIssue } from './gitea-helper.js'
+import GiteaBase from './gitea-base.js'
+
+const schema = Joi.object({ 'x-total-count': nonNegativeInteger }).required()
+
+const queryParamSchema = Joi.object({
+ labels: Joi.string(),
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaIssues extends GiteaBase {
+ static category = 'issue-tracking'
+
+ static route = {
+ base: 'gitea/issues',
+ pattern:
+ ':variant(all|all-raw|open|open-raw|closed|closed-raw)/:user/:repo+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/issues/{variant}/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Issues',
+ description,
+ parameters: [
+ pathParam({
+ name: 'variant',
+ example: 'all',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ }),
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ queryParam({
+ name: 'labels',
+ example: 'test,failure::new',
+ description:
+ 'If you want to use multiple labels, you can use a comma (,) to separate them, e.g. foo,bar',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'issues', color: 'informational' }
+ async handle(
+ { variant, user, repo },
+ { gitea_url: baseUrl = 'https://gitea.com', labels },
+ ) {
+ const options = {
+ searchParams: {
+ page: '1',
+ limit: '1',
+ type: 'issues',
+ state: variant.replace('-raw', ''),
+ },
+ }
+ if (labels) {
+ options.searchParams.labels = labels
+ }
+
+ const { res } = await fetchIssue(this, {
+ user,
+ repo,
+ baseUrl,
+ options,
+ httpErrors: httpErrorsFor(),
+ })
+
+ const data = this.constructor._validate(res.headers, schema)
+ // The total number of issues is in the `x-total-count` field in the headers.
+ // Pull requests are an issue of type pulls
+ // https://gitea.com/api/swagger#/issue
+ const count = data['x-total-count']
+ return renderIssue({
+ variant,
+ labels,
+ defaultBadgeData: this.constructor.defaultBadgeData,
+ count,
+ })
+ }
+}
diff --git a/services/gitea/gitea-issues.tester.js b/services/gitea/gitea-issues.tester.js
new file mode 100644
index 0000000000000..cfbf71133548e
--- /dev/null
+++ b/services/gitea/gitea-issues.tester.js
@@ -0,0 +1,167 @@
+import { createServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricOpenIssues,
+ isMetricClosedIssues,
+ isMetricWithPattern,
+} from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Issues (project not found)')
+ .get('/open/CanisHelix/do-not-exist.json')
+ .expectBadge({
+ label: 'issues',
+ message: 'user or repo not found',
+ })
+
+/**
+ * Opened issue number case
+ */
+t.create('Opened issues')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues raw')
+ .get(
+ '/open-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'open issues',
+ message: isMetric,
+ })
+
+t.create('Open issues by label is > zero')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question',
+ )
+ .expectBadge({
+ label: 'question issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues by multi-word label is > zero')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question,enhancement',
+ )
+ .expectBadge({
+ label: 'question,enhancement issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues by label (raw)')
+ .get(
+ '/open-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question',
+ )
+ .expectBadge({
+ label: 'open question issues',
+ message: isMetric,
+ })
+
+t.create('Opened issues by Scoped labels')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question,enhancement/new',
+ )
+ .expectBadge({
+ label: 'question,enhancement/new issues',
+ message: isMetricOpenIssues,
+ })
+
+/**
+ * Closed issue number case
+ */
+t.create('Closed issues')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues raw')
+ .get(
+ '/closed-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'closed issues',
+ message: isMetric,
+ })
+
+t.create('Closed issues by label is > zero')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug',
+ )
+ .expectBadge({
+ label: 'bug issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues by multi-word label is > zero')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug,good%20first%20issue',
+ )
+ .expectBadge({
+ label: 'bug,good first issue issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues by label (raw)')
+ .get(
+ '/closed-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug',
+ )
+ .expectBadge({
+ label: 'closed bug issues',
+ message: isMetric,
+ })
+
+/**
+ * All issue number case
+ */
+t.create('All issues')
+ .get('/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'issues',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All issues raw')
+ .get(
+ '/all-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'all issues',
+ message: isMetric,
+ })
+
+t.create('All issues by label is > zero')
+ .get(
+ '/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question',
+ )
+ .expectBadge({
+ label: 'question issues',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All issues by multi-word label is > zero')
+ .get(
+ '/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question,enhancement',
+ )
+ .expectBadge({
+ label: 'question,enhancement issues',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All issues by label (raw)')
+ .get(
+ '/all-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=question',
+ )
+ .expectBadge({
+ label: 'all question issues',
+ message: isMetric,
+ })
diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js
new file mode 100644
index 0000000000000..31b68435b5454
--- /dev/null
+++ b/services/gitea/gitea-languages-count.service.js
@@ -0,0 +1,76 @@
+import Joi from 'joi'
+import { nonNegativeInteger, optionalUrl } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { pathParam, queryParam } from '../index.js'
+import { description, httpErrorsFor } from './gitea-helper.js'
+import GiteaBase from './gitea-base.js'
+
+/*
+We're expecting a response like { "Python": 39624, "Shell": 104 }
+The keys could be anything and {} is a valid response (e.g: for an empty repo)
+*/
+const schema = Joi.object().pattern(/./, nonNegativeInteger)
+
+const queryParamSchema = Joi.object({
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaLanguageCount extends GiteaBase {
+ static category = 'analysis'
+
+ static route = {
+ base: 'gitea/languages/count',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/languages/count/{user}/{repo}': {
+ get: {
+ summary: 'Gitea language count',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'languages' }
+
+ static render({ languagesCount }) {
+ return {
+ message: metric(languagesCount),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ user, repo, baseUrl }) {
+ // https://gitea.com/api/swagger#/repository/repoGetLanguages
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`,
+ httpErrors: httpErrorsFor(),
+ })
+ }
+
+ async handle({ user, repo }, { gitea_url: baseUrl = 'https://gitea.com' }) {
+ const data = await this.fetch({
+ user,
+ repo,
+ baseUrl,
+ })
+ return this.constructor.render({ languagesCount: Object.keys(data).length })
+ }
+}
diff --git a/services/gitea/gitea-languages-count.tester.js b/services/gitea/gitea-languages-count.tester.js
new file mode 100644
index 0000000000000..fe94a2fe905f7
--- /dev/null
+++ b/services/gitea/gitea-languages-count.tester.js
@@ -0,0 +1,32 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('language count').get('/gitea/tea.json').expectBadge({
+ label: 'languages',
+ message: Joi.number().integer().positive(),
+})
+
+t.create('language count (empty repo) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'languages',
+ message: '0',
+ })
+
+t.create('language count (self-managed)')
+ .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'languages',
+ message: Joi.number().integer().positive(),
+ })
+
+t.create('language count (user or repo not found) (self-managed)')
+ .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'languages',
+ message: 'user or repo not found',
+ })
diff --git a/services/gitea/gitea-last-commit.service.js b/services/gitea/gitea-last-commit.service.js
new file mode 100644
index 0000000000000..edf33a06645fe
--- /dev/null
+++ b/services/gitea/gitea-last-commit.service.js
@@ -0,0 +1,143 @@
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, relativeUri } from '../validators.js'
+import GiteaBase from './gitea-base.js'
+import { description, httpErrorsFor } from './gitea-helper.js'
+
+const schema = Joi.array()
+ .items(
+ Joi.object({
+ commit: Joi.object({
+ author: Joi.object({
+ date: Joi.string().required(),
+ }).required(),
+ committer: Joi.object({
+ date: Joi.string().required(),
+ }).required(),
+ }).required(),
+ }).required(),
+ )
+ .required()
+ .min(1)
+
+const displayEnum = ['author', 'committer']
+
+const queryParamSchema = Joi.object({
+ path: relativeUri,
+ display_timestamp: Joi.string()
+ .valid(...displayEnum)
+ .default('author'),
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaLastCommit extends GiteaBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'gitea/last-commit',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/last-commit/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Last Commit',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ queryParam({
+ name: 'display_timestamp',
+ example: 'committer',
+ schema: { type: 'string', enum: displayEnum },
+ description: 'Defaults to `author` if not specified',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ ],
+ },
+ },
+ '/gitea/last-commit/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Gitea Last Commit (branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'main',
+ }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ queryParam({
+ name: 'display_timestamp',
+ example: 'committer',
+ schema: { type: 'string', enum: displayEnum },
+ description: 'Defaults to `author` if not specified',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last commit' }
+
+ async fetch({ user, repo, branch, baseUrl, path }) {
+ // https://gitea.com/api/swagger#/repository
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}/commits`,
+ options: { searchParams: { sha: branch, path, limit: 1 } },
+ httpErrors: httpErrorsFor('user, repo or path not found'),
+ })
+ }
+
+ async handle(
+ { user, repo, branch },
+ {
+ gitea_url: baseUrl = 'https://gitea.com',
+ display_timestamp: displayTimestamp,
+ path,
+ },
+ ) {
+ const body = await this.fetch({
+ user,
+ repo,
+ branch,
+ baseUrl,
+ path,
+ })
+ return renderDateBadge(body[0].commit[displayTimestamp].date)
+ }
+}
diff --git a/services/gitea/gitea-last-commit.tester.js b/services/gitea/gitea-last-commit.tester.js
new file mode 100644
index 0000000000000..f77ac6af27b60
--- /dev/null
+++ b/services/gitea/gitea-last-commit.tester.js
@@ -0,0 +1,81 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Last Commit (recent)').get('/gitea/tea.json').expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+})
+
+t.create('Last Commit (recent) (top-level file path)')
+ .get('/gitea/tea.json?path=README.md')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (recent) (top-level dir path)')
+ .get('/gitea/tea.json?path=docs')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (recent) (top-level dir path with trailing slash)')
+ .get('/gitea/tea.json?path=docs/')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (recent) (nested dir path)')
+ .get('/gitea/tea.json?path=docs/CLI.md')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (recent) (path)')
+ .get('/gitea/tea.json?path=README.md')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (recent) (self-managed)')
+ .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (on-branch) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test/scoped.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('Last Commit (user not found)')
+ .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or path not found',
+ })
+
+t.create('Last Commit (repo not found)')
+ .get('/gitea/not-a-repo.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or path not found',
+ })
+
+t.create('Last Commit (path not found)')
+ .get('/gitea/tea.json?path=not/a/dir')
+ .expectBadge({
+ label: 'last commit',
+ message: 'user, repo or path not found',
+ })
diff --git a/services/gitea/gitea-pull-requests.service.js b/services/gitea/gitea-pull-requests.service.js
new file mode 100644
index 0000000000000..5b00f2485e218
--- /dev/null
+++ b/services/gitea/gitea-pull-requests.service.js
@@ -0,0 +1,97 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { fetchIssue } from './gitea-common-fetch.js'
+import { description, httpErrorsFor, renderIssue } from './gitea-helper.js'
+import GiteaBase from './gitea-base.js'
+
+const schema = Joi.object({ 'x-total-count': nonNegativeInteger }).required()
+
+const queryParamSchema = Joi.object({
+ labels: Joi.string(),
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaPullRequests extends GiteaBase {
+ static category = 'issue-tracking'
+
+ static route = {
+ base: 'gitea/pull-requests',
+ pattern:
+ ':variant(all|all-raw|open|open-raw|closed|closed-raw)/:user/:repo+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/pull-requests/{variant}/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Pull Requests',
+ description,
+ parameters: [
+ pathParam({
+ name: 'variant',
+ example: 'all',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ }),
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ queryParam({
+ name: 'labels',
+ example: 'test,failure::new',
+ description:
+ 'If you want to use multiple labels, you can use a comma (,) to separate them, e.g. foo,bar',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'pull requests', color: 'informational' }
+
+ async handle(
+ { variant, user, repo },
+ { gitea_url: baseUrl = 'https://gitea.com', labels },
+ ) {
+ const options = {
+ searchParams: {
+ page: '1',
+ limit: '1',
+ type: 'pulls',
+ state: variant.replace('-raw', ''),
+ },
+ }
+ if (labels) {
+ options.searchParams.labels = labels
+ }
+
+ const { res } = await fetchIssue(this, {
+ user,
+ repo,
+ baseUrl,
+ options,
+ httpErrors: httpErrorsFor(),
+ })
+
+ const data = this.constructor._validate(res.headers, schema)
+ // The total number of issues is in the `x-total-count` field in the headers.
+ // Pull requests are an issue of type pulls
+ // https://gitea.com/api/swagger#/issue
+ const count = data['x-total-count']
+ return renderIssue({
+ variant,
+ labels,
+ defaultBadgeData: this.constructor.defaultBadgeData,
+ count,
+ })
+ }
+}
diff --git a/services/gitea/gitea-pull-requests.tester.js b/services/gitea/gitea-pull-requests.tester.js
new file mode 100644
index 0000000000000..a2849fbc7e9aa
--- /dev/null
+++ b/services/gitea/gitea-pull-requests.tester.js
@@ -0,0 +1,167 @@
+import { createServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricOpenIssues,
+ isMetricClosedIssues,
+ isMetricWithPattern,
+} from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Pulls (project not found)')
+ .get('/open/CanisHelix/do-not-exist.json')
+ .expectBadge({
+ label: 'pull requests',
+ message: 'user or repo not found',
+ })
+
+/**
+ * Opened pulls number case
+ */
+t.create('Opened pulls')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open pulls raw')
+ .get(
+ '/open-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'open pull requests',
+ message: isMetric,
+ })
+
+t.create('Open pulls by label is > zero')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream',
+ )
+ .expectBadge({
+ label: 'upstream pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open pulls by multi-word label is > zero')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream,enhancement',
+ )
+ .expectBadge({
+ label: 'upstream,enhancement pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open pulls by label (raw)')
+ .get(
+ '/open-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream',
+ )
+ .expectBadge({
+ label: 'open upstream pull requests',
+ message: isMetric,
+ })
+
+t.create('Opened pulls by Scoped label')
+ .get(
+ '/open/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=failure/new',
+ )
+ .expectBadge({
+ label: 'failure/new pull requests',
+ message: isMetricOpenIssues,
+ })
+
+/**
+ * Closed pulls number case
+ */
+t.create('Closed pulls')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed pulls raw')
+ .get(
+ '/closed-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'closed pull requests',
+ message: isMetric,
+ })
+
+t.create('Closed pulls by label is > zero')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug',
+ )
+ .expectBadge({
+ label: 'bug pull requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed pulls by multi-word label is > zero')
+ .get(
+ '/closed/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug,good%20first%20issue',
+ )
+ .expectBadge({
+ label: 'bug,good first issue pull requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed pulls by label (raw)')
+ .get(
+ '/closed-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=bug',
+ )
+ .expectBadge({
+ label: 'closed bug pull requests',
+ message: isMetric,
+ })
+
+/**
+ * All pulls number case
+ */
+t.create('All pulls')
+ .get('/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All pulls raw')
+ .get(
+ '/all-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({
+ label: 'all pull requests',
+ message: isMetric,
+ })
+
+t.create('All pulls by label is > zero')
+ .get(
+ '/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream',
+ )
+ .expectBadge({
+ label: 'upstream pull requests',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All pulls by multi-word label is > zero')
+ .get(
+ '/all/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream,enhancement',
+ )
+ .expectBadge({
+ label: 'upstream,enhancement pull requests',
+ message: isMetricWithPattern(/ all/),
+ })
+
+t.create('All pulls by label (raw)')
+ .get(
+ '/all-raw/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&labels=upstream',
+ )
+ .expectBadge({
+ label: 'all upstream pull requests',
+ message: isMetric,
+ })
diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js
new file mode 100644
index 0000000000000..8a6b2817ec8c4
--- /dev/null
+++ b/services/gitea/gitea-release.service.js
@@ -0,0 +1,146 @@
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { latest, renderVersionBadge } from '../version.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { description, httpErrorsFor } from './gitea-helper.js'
+import GiteaBase from './gitea-base.js'
+
+const schema = Joi.array().items(
+ Joi.object({
+ name: Joi.string().required(),
+ tag_name: Joi.string().required(),
+ prerelease: Joi.boolean().required(),
+ }),
+)
+
+const sortEnum = ['date', 'semver']
+const displayNameEnum = ['tag', 'release']
+const dateOrderByEnum = ['created_at', 'published_at']
+
+const queryParamSchema = Joi.object({
+ gitea_url: optionalUrl,
+ include_prereleases: Joi.equal(''),
+ sort: Joi.string()
+ .valid(...sortEnum)
+ .default('date'),
+ display_name: Joi.string()
+ .valid(...displayNameEnum)
+ .default('tag'),
+ date_order_by: Joi.string()
+ .valid(...dateOrderByEnum)
+ .default('created_at'),
+}).required()
+
+export default class GiteaRelease extends GiteaBase {
+ static category = 'version'
+
+ static route = {
+ base: 'gitea/v/release',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/v/release/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Release',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ queryParam({
+ name: 'sort',
+ schema: { type: 'string', enum: sortEnum },
+ example: 'semver',
+ }),
+ queryParam({
+ name: 'display_name',
+ schema: { type: 'string', enum: displayNameEnum },
+ example: 'release',
+ }),
+ queryParam({
+ name: 'date_order_by',
+ schema: { type: 'string', enum: dateOrderByEnum },
+ example: 'created_at',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'release' }
+
+ async fetch({ user, repo, baseUrl }) {
+ // https://gitea.com/api/swagger#/repository/repoGetRelease
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`,
+ httpErrors: httpErrorsFor(),
+ })
+ }
+
+ static transform({ releases, isSemver, includePrereleases, displayName }) {
+ if (releases.length === 0) {
+ throw new NotFound({ prettyMessage: 'no releases found' })
+ }
+
+ const displayKey = displayName === 'tag' ? 'tag_name' : 'name'
+
+ if (isSemver) {
+ return latest(
+ releases.map(t => t[displayKey]),
+ { pre: includePrereleases },
+ )
+ }
+
+ if (!includePrereleases) {
+ const stableReleases = releases.filter(release => !release.prerelease)
+ if (stableReleases.length > 0) {
+ return stableReleases[0][displayKey]
+ }
+ }
+
+ return releases[0][displayKey]
+ }
+
+ async handle(
+ { user, repo },
+ {
+ gitea_url: baseUrl = 'https://gitea.com',
+ include_prereleases: pre,
+ sort,
+ display_name: displayName,
+ date_order_by: orderBy,
+ },
+ ) {
+ const isSemver = sort === 'semver'
+ const releases = await this.fetch({
+ user,
+ repo,
+ baseUrl,
+ isSemver,
+ })
+ const version = this.constructor.transform({
+ releases,
+ isSemver,
+ includePrereleases: pre !== undefined,
+ displayName,
+ })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/gitea/gitea-release.tester.js b/services/gitea/gitea-release.tester.js
new file mode 100644
index 0000000000000..9c7602997e8ce
--- /dev/null
+++ b/services/gitea/gitea-release.tester.js
@@ -0,0 +1,49 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Release (latest by date)')
+ .get('/gitea/tea.json')
+ .expectBadge({
+ label: 'release',
+ message: Joi.string(),
+ color: Joi.any().valid(...['orange', 'blue']),
+ })
+
+t.create('Release (latest by date) (self-managed)')
+ .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
+
+t.create('Release (latest by date, order by created_at) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=created_at',
+ )
+ .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
+
+t.create('Release (latest by date, order by published_at) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=published_at',
+ )
+ .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
+
+t.create('Release (latest by semver) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver',
+ )
+ .expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' })
+
+t.create('Release (latest by semver pre-release) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver&include_prereleases',
+ )
+ .expectBadge({ label: 'release', message: 'v5.0.0-rc1', color: 'orange' })
+
+t.create('Release (project not found) (self-managed)')
+ .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
+ .expectBadge({ label: 'release', message: 'user or repo not found' })
+
+t.create('Release (no tags) (self-managed)')
+ .get(
+ '/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org',
+ )
+ .expectBadge({ label: 'release', message: 'no releases found' })
diff --git a/services/gitea/gitea-stars.service.js b/services/gitea/gitea-stars.service.js
new file mode 100644
index 0000000000000..43732b4068e59
--- /dev/null
+++ b/services/gitea/gitea-stars.service.js
@@ -0,0 +1,76 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import GiteaBase from './gitea-base.js'
+import { description, httpErrorsFor } from './gitea-helper.js'
+
+const schema = Joi.object({
+ stars_count: nonNegativeInteger,
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitea_url: optionalUrl,
+}).required()
+
+export default class GiteaStars extends GiteaBase {
+ static category = 'social'
+
+ static route = {
+ base: 'gitea/stars',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitea/stars/{user}/{repo}': {
+ get: {
+ summary: 'Gitea Stars',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitea',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'tea',
+ }),
+ queryParam({
+ name: 'gitea_url',
+ example: 'https://gitea.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'stars', namedLogo: 'gitea' }
+
+ static render({ baseUrl, user, repo, starCount }) {
+ return {
+ message: metric(starCount),
+ style: 'social',
+ color: 'blue',
+ link: [`${baseUrl}/${user}/${repo}`, `${baseUrl}/${user}/${repo}/stars`],
+ }
+ }
+
+ async fetch({ user, repo, baseUrl }) {
+ // https://gitea.com/api/swagger#/repository
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v1/repos/${user}/${repo}`,
+ httpErrors: httpErrorsFor(),
+ })
+ }
+
+ async handle({ user, repo }, { gitea_url: baseUrl = 'https://gitea.com' }) {
+ const { stars_count: starCount } = await this.fetch({
+ user,
+ repo,
+ baseUrl,
+ })
+ return this.constructor.render({ baseUrl, user, repo, starCount })
+ }
+}
diff --git a/services/gitea/gitea-stars.tester.js b/services/gitea/gitea-stars.tester.js
new file mode 100644
index 0000000000000..bec444bfa74e3
--- /dev/null
+++ b/services/gitea/gitea-stars.tester.js
@@ -0,0 +1,32 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Stars')
+ .get('/gitea/tea.json')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ color: 'blue',
+ link: ['https://gitea.com/gitea/tea', 'https://gitea.com/gitea/tea/stars'],
+ })
+
+t.create('Stars (self-managed)')
+ .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://codeberg.org/CanisHelix/shields-badge-test',
+ 'https://codeberg.org/CanisHelix/shields-badge-test/stars',
+ ],
+ })
+
+t.create('Stars (project not found)')
+ .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
+ .expectBadge({
+ label: 'stars',
+ message: 'user or repo not found',
+ })
diff --git a/services/github/auth/acceptor.js b/services/github/auth/acceptor.js
index 9b5ae42f33762..f74adaffc549d 100644
--- a/services/github/auth/acceptor.js
+++ b/services/github/auth/acceptor.js
@@ -1,86 +1,42 @@
-'use strict'
-
-const queryString = require('query-string')
-const request = require('request')
-const {
- userAgent,
-} = require('../../../core/base-service/legacy-request-handler')
-const log = require('../../../core/server/log')
-const secretIsValid = require('../../../core/server/secret-is-valid')
-const serverSecrets = require('../../../lib/server-secrets')
-
-function sendTokenToAllServers(token) {
- const {
- shields_ips: shieldsIps,
- shields_secret: shieldsSecret,
- } = serverSecrets
- return Promise.all(
- shieldsIps.map(
- ip =>
- new Promise((resolve, reject) => {
- const options = {
- url: `https://${ip}/github-auth/add-token`,
- method: 'POST',
- form: {
- shieldsSecret,
- token,
- },
- // We target servers by IP, and we use HTTPS. Assuming that
- // 1. Internet routers aren't hacked, and
- // 2. We don't unknowingly lose our IP to someone else,
- // we're not leaking people's and our information.
- // (If we did, it would have no impact, as we only ask for a token,
- // no GitHub scope. The malicious entity would only be able to use
- // our rate limit pool.)
- // FIXME: use letsencrypt.
- strictSSL: false,
- }
- request(options, (err, res, body) => {
- if (err != null) {
- reject(err)
- } else {
- resolve()
- }
- })
- })
- )
- )
-}
+import qs from 'qs'
+import { fetch } from '../../../core/base-service/got.js'
+import log from '../../../core/server/log.js'
function setRoutes({ server, authHelper, onTokenAccepted }) {
- const baseUrl = process.env.GATSBY_BASE_URL || 'https://img.shields.io'
+ const baseUrl = 'https://img.shields.io'
server.route(/^\/github-auth$/, (data, match, end, ask) => {
ask.res.statusCode = 302 // Found.
- const query = queryString.stringify({
+ const query = qs.stringify({
// TODO The `_user` property bypasses security checks in AuthHelper.
// (e.g: enforceStrictSsl and shouldAuthenticateRequest).
// Do not use it elsewhere. It would be better to clean this up so
// it's not setting a bad example.
client_id: authHelper._user,
redirect_uri: `${baseUrl}/github-auth/done`,
+ // TODO: Request the OAuth scopes needed for scoped GitHub API features.
+ // Remove this TODO once this authorize URL includes a `scope` parameter.
+ // See https://github.com/badges/shields/issues/4169.
})
ask.res.setHeader(
'Location',
- `https://github.com/login/oauth/authorize?${query}`
+ `https://github.com/login/oauth/authorize?${query}`,
)
end('')
})
- server.route(/^\/github-auth\/done$/, (data, match, end, ask) => {
+ server.route(/^\/github-auth\/done$/, async (data, match, end, ask) => {
if (!data.code) {
- log(`GitHub OAuth data: ${JSON.stringify(data)}`)
+ log.log(`GitHub OAuth data: ${JSON.stringify(data)}`)
return end('GitHub OAuth authentication failed to provide a code.')
}
const options = {
- url: 'https://github.com/login/oauth/access_token',
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
- 'User-Agent': userAgent,
},
- form: queryString.stringify({
+ form: {
// TODO The `_user` and `_pass` properties bypass security checks in
// AuthHelper (e.g: enforceStrictSsl and shouldAuthenticateRequest).
// Do not use them elsewhere. It would be better to clean
@@ -88,58 +44,47 @@ function setRoutes({ server, authHelper, onTokenAccepted }) {
client_id: authHelper._user,
client_secret: authHelper._pass,
code: data.code,
- }),
+ },
}
- request(options, (err, res, body) => {
- if (err != null) {
- return end('The connection to GitHub failed.')
- }
-
- let content
- try {
- content = queryString.parse(body)
- } catch (e) {
- return end('The GitHub OAuth token could not be parsed.')
- }
-
- const { access_token: token } = content
- if (!token) {
- return end('The GitHub OAuth process did not return a user token.')
- }
- ask.res.setHeader('Content-Type', 'text/html')
- end(
- 'Shields.io has received your app-specific GitHub user token. ' +
- 'You can revoke it by going to ' +
- 'GitHub .
' +
- 'Until you do, you have now increased the rate limit for GitHub ' +
- 'requests going through Shields.io. GitHub-related badges are ' +
- 'therefore more robust.
' +
- 'Thanks for contributing to a smoother experience for ' +
- 'everyone!
' +
- 'Back to the website
'
- )
+ let resp
+ try {
+ resp = await fetch('https://github.com/login/oauth/access_token', options)
+ } catch (e) {
+ return end('The connection to GitHub failed.')
+ }
- sendTokenToAllServers(token).catch(e => {
- console.error('GitHub user token transmission failed:', e)
- })
- })
- })
+ let content
+ try {
+ content = qs.parse(resp.buffer)
+ } catch (e) {
+ return end('The GitHub OAuth token could not be parsed.')
+ }
- server.route(/^\/github-auth\/add-token$/, (data, match, end, ask) => {
- if (!secretIsValid(data.shieldsSecret)) {
- // An unknown entity tries to connect. Let the connection linger for 10s.
- setTimeout(() => {
- end('Invalid secret.')
- }, 10000)
- return
+ const { access_token: token, scope = '' } = content
+ if (!token) {
+ return end('The GitHub OAuth process did not return a user token.')
}
+ const scopes = scope
+ .split(',')
+ .map(scope => scope.trim())
+ .filter(Boolean)
- onTokenAccepted(data.token)
- end('Thanks!')
+ ask.res.setHeader('Content-Type', 'text/html')
+ end(
+ 'Shields.io has received your app-specific GitHub user token. ' +
+ 'You can revoke it by going to ' +
+ 'GitHub .
' +
+ 'Until you do, you have now increased the rate limit for GitHub ' +
+ 'requests going through Shields.io. GitHub-related badges are ' +
+ 'therefore more robust.
' +
+ 'Thanks for contributing to a smoother experience for ' +
+ 'everyone!
' +
+ 'Back to the website
',
+ )
+
+ onTokenAccepted(token, { scopes })
})
}
-module.exports = {
- setRoutes,
-}
+export { setRoutes }
diff --git a/services/github/auth/acceptor.spec.js b/services/github/auth/acceptor.spec.js
index 3a7663fa8e0fb..03b5c4511b3e6 100644
--- a/services/github/auth/acceptor.spec.js
+++ b/services/github/auth/acceptor.spec.js
@@ -1,47 +1,34 @@
-'use strict'
-
-const { expect } = require('chai')
-const Camp = require('@shields_io/camp')
-const sinon = require('sinon')
-const portfinder = require('portfinder')
-const queryString = require('query-string')
-const nock = require('nock')
-const got = require('../../../core/got-test-client')
-const serverSecrets = require('../../../lib/server-secrets')
-const GithubConstellation = require('../github-constellation')
-const acceptor = require('./acceptor')
+import { expect } from 'chai'
+import Camp from '@shields_io/camp'
+import sinon from 'sinon'
+import portfinder from 'portfinder'
+import qs from 'qs'
+import nock from 'nock'
+import '../../../core/register-chai-plugins.spec.js'
+import got from '../../../core/got-test-client.js'
+import GithubConstellation from '../github-constellation.js'
+import { setRoutes } from './acceptor.js'
const fakeClientId = 'githubdabomb'
-const fakeShieldsSecret = 'letmeinplz'
+const fakeClientSecret = 'foobar'
-describe('Github token acceptor', function() {
+describe('Github token acceptor', function () {
const oauthHelper = GithubConstellation._createOauthHelper({
- private: { gh_client_id: fakeClientId },
- })
- before(function() {
- // Make sure properties exist.
- // https://github.com/sinonjs/sinon/pull/1557
- serverSecrets.shields_ips = undefined
- serverSecrets.shields_secret = undefined
- sinon.stub(serverSecrets, 'shields_ips').value([])
- sinon.stub(serverSecrets, 'shields_secret').value(fakeShieldsSecret)
- })
- after(function() {
- sinon.restore()
+ private: { gh_client_id: fakeClientId, gh_client_secret: fakeClientSecret },
})
let port, baseUrl
- beforeEach(async function() {
+ beforeEach(async function () {
port = await portfinder.getPortPromise()
baseUrl = `http://127.0.0.1:${port}`
})
let camp
- beforeEach(async function() {
+ beforeEach(async function () {
camp = Camp.start({ port, hostname: '::' })
await new Promise(resolve => camp.on('listening', () => resolve()))
})
- afterEach(async function() {
+ afterEach(async function () {
if (camp) {
await new Promise(resolve => camp.close(resolve))
camp = undefined
@@ -49,34 +36,34 @@ describe('Github token acceptor', function() {
})
let onTokenAccepted
- beforeEach(function() {
+ beforeEach(function () {
onTokenAccepted = sinon.stub()
- acceptor.setRoutes({
+ setRoutes({
server: camp,
authHelper: oauthHelper,
onTokenAccepted,
})
})
- it('should start the OAuth process', async function() {
+ it('should start the OAuth process', async function () {
const res = await got(`${baseUrl}/github-auth`, { followRedirect: false })
expect(res.statusCode).to.equal(302)
- const qs = queryString.stringify({
+ const queryString = qs.stringify({
client_id: fakeClientId,
redirect_uri: 'https://img.shields.io/github-auth/done',
})
- const expectedLocationHeader = `https://github.com/login/oauth/authorize?${qs}`
+ const expectedLocationHeader = `https://github.com/login/oauth/authorize?${queryString}`
expect(res.headers.location).to.equal(expectedLocationHeader)
})
- describe('Finishing the OAuth process', function() {
- context('no code is provided', function() {
- it('should return an error', async function() {
+ describe('Finishing the OAuth process', function () {
+ context('no code is provided', function () {
+ it('should return an error', async function () {
const res = await got(`${baseUrl}/github-auth/done`)
expect(res.body).to.equal(
- 'GitHub OAuth authentication failed to provide a code.'
+ 'GitHub OAuth authentication failed to provide a code.',
)
})
})
@@ -84,60 +71,70 @@ describe('Github token acceptor', function() {
const fakeCode = '123456789'
const fakeAccessToken = 'abcdef'
- context('a code is provided', function() {
+ context('a code is provided', function () {
let scope
- beforeEach(function() {
+ let tokenResponse
+ beforeEach(function () {
nock.enableNetConnect(/127\.0\.0\.1/)
+ tokenResponse = { access_token: fakeAccessToken }
scope = nock('https://github.com')
.post('/login/oauth/access_token')
.reply((url, requestBody) => {
- expect(queryString.parse(requestBody).code).to.equal(fakeCode)
- return [
- 200,
- queryString.stringify({ access_token: fakeAccessToken }),
- ]
+ const parsedBody = qs.parse(requestBody)
+ expect(parsedBody.client_id).to.equal(fakeClientId)
+ expect(parsedBody.client_secret).to.equal(fakeClientSecret)
+ expect(parsedBody.code).to.equal(fakeCode)
+ return [200, qs.stringify(tokenResponse)]
})
})
- afterEach(function() {
+ afterEach(function () {
// Make sure other tests will make live requests even when this test
// fails.
nock.enableNetConnect()
})
- afterEach(function() {
+ afterEach(function () {
if (scope) {
scope.done()
scope = null
}
})
- afterEach(function() {
+ afterEach(function () {
nock.cleanAll()
})
- it('should finish the OAuth process', async function() {
- const res = await got(`${baseUrl}/github-auth/done`, {
- form: true,
- body: { code: fakeCode },
+ it('should finish the OAuth process', async function () {
+ const form = new FormData()
+ form.append('code', fakeCode)
+
+ const res = await got.post(`${baseUrl}/github-auth/done`, {
+ body: form,
})
- expect(res.body).to.startWith(
- 'Shields.io has received your app-specific GitHub user token.'
- )
+ expect(
+ res.body.startsWith(
+ '
Shields.io has received your app-specific GitHub user token.',
+ ),
+ ).to.be.true
+ expect(onTokenAccepted).to.have.been.calledWith(fakeAccessToken)
})
- })
- })
- it('should add a received token', async function() {
- const fakeAccessToken = 'its-my-token'
+ it('should pass token scopes from the OAuth response', async function () {
+ tokenResponse.scope = 'read:packages, read:user'
- const { body } = await got(`${baseUrl}/github-auth/add-token`, {
- form: true,
- body: { shieldsSecret: fakeShieldsSecret, token: fakeAccessToken },
- })
+ const form = new FormData()
+ form.append('code', fakeCode)
+
+ await got.post(`${baseUrl}/github-auth/done`, {
+ body: form,
+ })
- expect(onTokenAccepted).to.have.been.calledWith(fakeAccessToken)
- expect(body).to.equal('Thanks!')
+ expect(onTokenAccepted).to.have.been.calledWith(fakeAccessToken, {
+ scopes: ['read:packages', 'read:user'],
+ })
+ })
+ })
})
})
diff --git a/services/github/auth/admin.js b/services/github/auth/admin.js
deleted file mode 100644
index 59fddc9e277ff..0000000000000
--- a/services/github/auth/admin.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict'
-
-const secretIsValid = require('../../../core/server/secret-is-valid')
-
-function setRoutes(apiProvider, server) {
- // Allow the admin to obtain the tokens for operational and debugging
- // purposes. This could be used to:
- //
- // - Ensure tokens have been propagated to all servers
- // - Debug GitHub badge failures
- //
- // The admin can authenticate with HTTP Basic Auth, with an empty/any
- // username and the shields secret in the password and an empty/any
- // password.
- //
- // e.g.
- // curl --insecure -u ':very-very-secret' 'https://s0.servers.shields.io/$github-auth/tokens'
- server.ajax.on('github-auth/tokens', (json, end, ask) => {
- if (!secretIsValid(ask.password)) {
- // An unknown entity tries to connect. Let the connection linger for a minute.
- return setTimeout(() => {
- end('Invalid secret.')
- }, 10000)
- }
- end(apiProvider.serializeDebugInfo({ sanitize: false }))
- })
-}
-
-module.exports = {
- setRoutes,
-}
diff --git a/services/github/auth/admin.spec.js b/services/github/auth/admin.spec.js
deleted file mode 100644
index 9d99ef545d8ce..0000000000000
--- a/services/github/auth/admin.spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const sinon = require('sinon')
-const Camp = require('@shields_io/camp')
-const portfinder = require('portfinder')
-const serverSecrets = require('../../../lib/server-secrets')
-const got = require('../../../core/got-test-client')
-const GithubApiProvider = require('../github-api-provider')
-const { setRoutes } = require('./admin')
-
-describe('GitHub admin route', function() {
- const validCredentials = {
- username: '',
- password: '7'.repeat(40),
- }
-
- let sandbox
- beforeEach(function() {
- sandbox = sinon.createSandbox()
- // Make this work when there is no `shields_secret` defined.
- serverSecrets.shields_secret = undefined
- sandbox
- .stub(serverSecrets, 'shields_secret')
- .value(validCredentials.password)
- })
- afterEach(function() {
- sandbox.restore()
- })
-
- let port, baseUrl
- before(async function() {
- port = await portfinder.getPortPromise()
- baseUrl = `http://127.0.0.1:${port}`
- })
-
- let camp
- before(async function() {
- camp = Camp.start({ port, hostname: '::' })
- await new Promise(resolve => camp.on('listening', () => resolve()))
- })
- after(async function() {
- if (camp) {
- await new Promise(resolve => camp.close(resolve))
- camp = undefined
- }
- })
-
- before(function() {
- const apiProvider = new GithubApiProvider({ withPooling: true })
- setRoutes(apiProvider, camp)
- })
-
- context('the password is correct', function() {
- it('returns a valid JSON response', async function() {
- const { username, password } = validCredentials
- const { statusCode, body } = await got(`${baseUrl}/$github-auth/tokens`, {
- auth: `${username}:${password}`,
- json: true,
- })
- expect(statusCode).to.equal(200)
- expect(body).to.be.ok
- })
- })
-
- // Disabled because this code isn't modified often and the test is very
- // slow. I wasn't able to make this work with fake timers:
- // https://github.com/sinonjs/sinon/issues/1739
- // context('the password is missing', function() {
- // it('returns the expected message', async function() {
- // this.timeout(11000)
- // const res = await fetch(`${baseUrl}/$github-auth/tokens`)
- // expect(res.ok).to.be.true
- // expect(await res.text()).to.equal('"Invalid secret."')
- // })
- // })
-})
diff --git a/services/github/auth/is-valid-token.js b/services/github/auth/is-valid-token.js
deleted file mode 100644
index bfb8b7f018fbf..0000000000000
--- a/services/github/auth/is-valid-token.js
+++ /dev/null
@@ -1,8 +0,0 @@
-'use strict'
-
-// This is only used by the TokenProviders, though probably the acceptor
-// should use it too.
-
-const isValidToken = t => /^[0-9a-f]{40}$/.test(t)
-
-module.exports = isValidToken
diff --git a/services/github/gist/github-gist-last-commit-redirect.service.js b/services/github/gist/github-gist-last-commit-redirect.service.js
new file mode 100644
index 0000000000000..bff653f0a7d39
--- /dev/null
+++ b/services/github/gist/github-gist-last-commit-redirect.service.js
@@ -0,0 +1,8 @@
+import { redirector } from '../../index.js'
+
+export default redirector({
+ category: 'activity',
+ route: { base: 'github-gist/last-commit', pattern: ':gistId' },
+ transformPath: ({ gistId }) => `/github/gist/last-commit/${gistId}`,
+ dateAdded: new Date('2022-10-09'),
+})
diff --git a/services/github/gist/github-gist-last-commit-redirect.tester.js b/services/github/gist/github-gist-last-commit-redirect.tester.js
new file mode 100644
index 0000000000000..a9b6d254f529d
--- /dev/null
+++ b/services/github/gist/github-gist-last-commit-redirect.tester.js
@@ -0,0 +1,13 @@
+import { ServiceTester } from '../../tester.js'
+
+export const t = new ServiceTester({
+ id: 'GistLastCommitRedirect',
+ title: 'GitHub Gist Last Commit Redirect',
+ pathPrefix: '/github-gist',
+})
+
+t.create('Last Commit redirect')
+ .get('/last-commit/a8b8c979d200ffde13cc08505f7a6436')
+ .expectRedirect(
+ '/github/gist/last-commit/a8b8c979d200ffde13cc08505f7a6436.svg',
+ )
diff --git a/services/github/gist/github-gist-last-commit.service.js b/services/github/gist/github-gist-last-commit.service.js
new file mode 100644
index 0000000000000..d090c9d3f2c36
--- /dev/null
+++ b/services/github/gist/github-gist-last-commit.service.js
@@ -0,0 +1,41 @@
+import Joi from 'joi'
+import { pathParams } from '../../index.js'
+import { renderDateBadge } from '../../date.js'
+import { GithubAuthV3Service } from '../github-auth-service.js'
+import { documentation, httpErrorsFor } from '../github-helpers.js'
+
+const schema = Joi.object({
+ updated_at: Joi.string().required(),
+}).required()
+
+export default class GistLastCommit extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = { base: 'github/gist/last-commit', pattern: ':gistId' }
+ static openApi = {
+ '/github/gist/last-commit/{gistId}': {
+ get: {
+ summary: 'GitHub Gist last commit',
+ description: `Shows the latest commit to a GitHub Gist.\n${documentation}`,
+ parameters: pathParams({
+ name: 'gistId',
+ example: '8710649',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last commit' }
+
+ async fetch({ gistId }) {
+ return this._requestJson({
+ url: `/gists/${gistId}`,
+ schema,
+ httpErrors: httpErrorsFor('gist not found'),
+ })
+ }
+
+ async handle({ gistId }) {
+ const { updated_at: commitDate } = await this.fetch({ gistId })
+ return renderDateBadge(commitDate)
+ }
+}
diff --git a/services/github/gist/github-gist-last-commit.tester.js b/services/github/gist/github-gist-last-commit.tester.js
new file mode 100644
index 0000000000000..5d82b48c333fd
--- /dev/null
+++ b/services/github/gist/github-gist-last-commit.tester.js
@@ -0,0 +1,24 @@
+import { createServiceTester } from '../../tester.js'
+export const t = await createServiceTester()
+
+t.create('last commit in gist (ancient)').get('/871064.json').expectBadge({
+ label: 'last commit',
+ message: 'september 2015',
+ color: 'red',
+})
+
+// not checking the color badge, since in August 2022 it is orange but later it will become red
+t.create('last commit in gist (still ancient but slightly less so)')
+ .get('/870071abadfd66a28bf539677332f12b.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'october 2020',
+ })
+
+t.create('last commit in gist (gist not found)')
+ .get('/55555555555555.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'gist not found',
+ color: 'red',
+ })
diff --git a/services/github/gist/github-gist-stars-redirect.service.js b/services/github/gist/github-gist-stars-redirect.service.js
new file mode 100644
index 0000000000000..8d2b4b8c1317f
--- /dev/null
+++ b/services/github/gist/github-gist-stars-redirect.service.js
@@ -0,0 +1,9 @@
+import { retiredService } from '../../index.js'
+
+export default retiredService({
+ category: 'social',
+ label: 'github',
+ route: { base: 'github/stars/gists', pattern: ':gistId' },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/github/gist/github-gist-stars-redirect.tester.js b/services/github/gist/github-gist-stars-redirect.tester.js
new file mode 100644
index 0000000000000..10142fe41e06c
--- /dev/null
+++ b/services/github/gist/github-gist-stars-redirect.tester.js
@@ -0,0 +1,13 @@
+import { ServiceTester } from '../../tester.js'
+export const t = new ServiceTester({
+ id: 'GistStarsRedirect',
+ title: 'Github Gist Stars Redirect',
+ pathPrefix: '/github',
+})
+
+t.create('Stars deprecated')
+ .get('/stars/gists/a8b8c979d200ffde13cc08505f7a6436.json')
+ .expectBadge({
+ label: 'github',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/github/gist/github-gist-stars.service.js b/services/github/gist/github-gist-stars.service.js
new file mode 100644
index 0000000000000..16769e93c2ce7
--- /dev/null
+++ b/services/github/gist/github-gist-stars.service.js
@@ -0,0 +1,117 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { metric } from '../../text-formatters.js'
+import { NotFound, pathParams } from '../../index.js'
+import { GithubAuthV4Service } from '../github-auth-service.js'
+import { documentation as commonDocumentation } from '../github-helpers.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ viewer: Joi.object({
+ gist: Joi.object({
+ stargazerCount: Joi.number().required(),
+ url: Joi.string().required(),
+ owner: Joi.object({
+ login: Joi.string().required(),
+ }).required(),
+ name: Joi.string().required(),
+ }).allow(null),
+ }).required(),
+ }).required(),
+}).required()
+
+const description = `${commonDocumentation}
+
+This badge shows the number of stargazers for a gist. Gist id is accepted as input and 'gist not found' is returned if the gist is not found for the given gist id.`
+
+export default class GistStars extends GithubAuthV4Service {
+ static category = 'social'
+
+ static route = {
+ base: 'github/gist/stars',
+ pattern: ':gistId',
+ }
+
+ static openApi = {
+ '/github/gist/stars/{gistId}': {
+ get: {
+ summary: 'GitHub Gist stars',
+ description,
+ parameters: pathParams({
+ name: 'gistId',
+ example: '47a4d00457a92aa426dbd48a18776322',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'Stars',
+ color: 'blue',
+ namedLogo: 'github',
+ }
+
+ static render({ stargazerCount, url, stargazers }) {
+ return {
+ message: metric(stargazerCount),
+ style: 'social',
+ link: [url, stargazers],
+ }
+ }
+
+ async fetch({ gistId }) {
+ const data = await this._requestGraphql({
+ query: gql`
+ query ($gistId: String!) {
+ viewer {
+ gist(name: $gistId) {
+ stargazerCount
+ url
+ name
+ owner {
+ login
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ gistId,
+ },
+ schema,
+ })
+ return data
+ }
+
+ static transform({ data }) {
+ const {
+ data: {
+ viewer: { gist },
+ },
+ } = data
+
+ if (!gist) {
+ throw new NotFound({ prettyMessage: 'gist not found' })
+ }
+
+ const {
+ stargazerCount,
+ url,
+ name,
+ owner: { login },
+ } = gist
+
+ const stargazers = `https://gist.github.com/${login}/${name}/stargazers`
+
+ return { stargazerCount, url, stargazers }
+ }
+
+ async handle({ gistId }) {
+ const data = await this.fetch({ gistId })
+ const { stargazerCount, url, stargazers } =
+ await this.constructor.transform({
+ data,
+ })
+ return this.constructor.render({ stargazerCount, url, stargazers })
+ }
+}
diff --git a/services/github/gist/github-gist-stars.tester.js b/services/github/gist/github-gist-stars.tester.js
new file mode 100644
index 0000000000000..2d88d407452a0
--- /dev/null
+++ b/services/github/gist/github-gist-stars.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../../tester.js'
+import { isMetric } from '../../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Gist Total Stars')
+ .get('/47a4d00457a92aa426dbd48a18776322.json')
+ .expectBadge({
+ label: 'Stars',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://gist.github.com/47a4d00457a92aa426dbd48a18776322',
+ 'https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322/stargazers',
+ ],
+ })
+
+t.create('Gist Total Stars (Not Found)')
+ .get('/invalid-gist-id.json')
+ .expectBadge({
+ label: 'Stars',
+ message: 'gist not found',
+ color: 'red',
+ link: [],
+ })
diff --git a/services/github/github-actions-workflow-status.service.js b/services/github/github-actions-workflow-status.service.js
new file mode 100644
index 0000000000000..d05c2476170de
--- /dev/null
+++ b/services/github/github-actions-workflow-status.service.js
@@ -0,0 +1,75 @@
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { BaseSvgScrapingService, pathParam, queryParam } from '../index.js'
+import { documentation } from './github-helpers.js'
+
+const schema = Joi.object({
+ message: Joi.alternatives()
+ .try(isBuildStatus, Joi.equal('no status'))
+ .required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ event: Joi.string(),
+ branch: Joi.alternatives().try(Joi.string(), Joi.number().cast('string')),
+}).required()
+
+export default class GithubActionsWorkflowStatus extends BaseSvgScrapingService {
+ static category = 'build'
+
+ static route = {
+ base: 'github/actions/workflow/status',
+ pattern: ':user/:repo/:workflow+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/actions/workflow/status/{user}/{repo}/{workflow}': {
+ get: {
+ summary: 'GitHub Actions Workflow Status',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'actions' }),
+ pathParam({ name: 'repo', example: 'toolkit' }),
+ pathParam({ name: 'workflow', example: 'unit-tests.yml' }),
+ queryParam({ name: 'branch', example: 'main' }),
+ queryParam({
+ name: 'event',
+ example: 'push',
+ description:
+ 'See GitHub Actions [Events that trigger workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) for allowed values.',
+ }),
+ ],
+ },
+ },
+ }
+
+ static _cacheLength = 60
+
+ static defaultBadgeData = {
+ label: 'build',
+ }
+
+ async fetch({ user, repo, workflow, branch, event }) {
+ const workflowPath = workflow
+ .split('/')
+ .map(el => encodeURIComponent(el))
+ .join('/')
+ const { message: status } = await this._requestSvg({
+ schema,
+ url: `https://github.com/${user}/${repo}/actions/workflows/${workflowPath}/badge.svg`,
+ options: { searchParams: { branch, event } },
+ valueMatcher: />([^<>]+)<\/tspan><\/text><\/g> {},
globalToken,
reserveFraction = 0.25,
+ restApiVersion,
+ maxTokenFailedAttempts = 3,
}) {
Object.assign(this, {
baseUrl,
- withPooling,
+ authType,
onTokenInvalidated,
globalToken,
reserveFraction,
+ maxTokenFailedAttempts,
})
- if (this.withPooling) {
+ this.metricInstance = undefined
+
+ if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {
this.standardTokens = new TokenPool({ batchSize: 25 })
this.searchTokens = new TokenPool({ batchSize: 5 })
this.graphqlTokens = new TokenPool({ batchSize: 25 })
}
+ this.restApiVersion = restApiVersion
}
- serializeDebugInfo({ sanitize = true } = {}) {
- if (this.withPooling) {
+ addToken(tokenString, data) {
+ if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {
+ this.standardTokens.add(tokenString, data)
+ this.searchTokens.add(tokenString, data)
+ this.graphqlTokens.add(tokenString, data)
+ } else {
+ throw Error('When not using a token pool, do not provide tokens')
+ }
+ }
+
+ getTokenDebugInfo({ sanitize = true } = {}) {
+ if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {
return {
standardTokens: this.standardTokens.serializeDebugInfo({ sanitize }),
searchTokens: this.searchTokens.serializeDebugInfo({ sanitize }),
@@ -68,16 +97,6 @@ class GithubApiProvider {
}
}
- addToken(tokenString) {
- if (this.withPooling) {
- this.standardTokens.add(tokenString)
- this.searchTokens.add(tokenString)
- this.graphqlTokens.add(tokenString)
- } else {
- throw Error('When not using a token pool, do not provide tokens')
- }
- }
-
getV3RateLimitFromHeaders(headers) {
const h = Joi.attempt(headers, headerSchema)
return {
@@ -88,8 +107,7 @@ class GithubApiProvider {
}
getV4RateLimitFromBody(body) {
- const parsedBody = JSON.parse(body)
- const b = Joi.attempt(parsedBody, bodySchema)
+ const b = Joi.attempt(body, bodySchema)
return {
rateLimit: b.data.rateLimit.limit,
totalUsesRemaining: b.data.rateLimit.remaining,
@@ -101,25 +119,28 @@ class GithubApiProvider {
let rateLimit, totalUsesRemaining, nextReset
if (url.startsWith('/graphql')) {
try {
- ;({
- rateLimit,
- totalUsesRemaining,
- nextReset,
- } = this.getV4RateLimitFromBody(res.body))
+ const parsedBody = JSON.parse(res.body)
+
+ if ('message' in parsedBody && !('data' in parsedBody)) {
+ if (parsedBody.message === 'Sorry. Your account was suspended.') {
+ this.invalidateToken(token, 'account_suspended')
+ return
+ }
+ }
+
+ ;({ rateLimit, totalUsesRemaining, nextReset } =
+ this.getV4RateLimitFromBody(parsedBody))
} catch (e) {
console.error(
- `Could not extract rate limit info from response body ${res.body}`
+ `Could not extract rate limit info from response body ${res.body}`,
)
log.error(e)
return
}
} else {
try {
- ;({
- rateLimit,
- totalUsesRemaining,
- nextReset,
- } = this.getV3RateLimitFromHeaders(res.headers))
+ ;({ rateLimit, totalUsesRemaining, nextReset } =
+ this.getV3RateLimitFromHeaders(res.headers))
} catch (e) {
const logHeaders = {
'x-ratelimit-limit': res.headers['x-ratelimit-limit'],
@@ -130,8 +151,8 @@ class GithubApiProvider {
`Invalid GitHub rate limit headers ${JSON.stringify(
logHeaders,
undefined,
- 2
- )}`
+ 2,
+ )}`,
)
log.error(e)
return
@@ -144,79 +165,98 @@ class GithubApiProvider {
token.update(usesRemaining, nextReset)
}
- invalidateToken(token) {
+ invalidateToken(token, reason) {
+ log.log(
+ `GitHub token invalidated and removed from pool (reason: ${reason}, token: ${sanitizeToken(
+ token.id,
+ )})`,
+ )
+ if (this.metricInstance) {
+ this.metricInstance.noteGithubTokenInvalidation({ reason })
+ }
token.invalidate()
this.onTokenInvalidated(token.id)
}
- tokenForUrl(url) {
+ poolForUrl(url) {
if (url.startsWith('/search')) {
- return this.searchTokens.next()
+ return this.searchTokens
} else if (url.startsWith('/graphql')) {
- return this.graphqlTokens.next()
+ return this.graphqlTokens
+ } else {
+ return this.standardTokens
+ }
+ }
+
+ // Handle a 401 for a token. A single 401 may be transient, so rather than
+ // evicting immediately, we count the failure, rotate to other tokens, and
+ // retry this one when it next comes around. Only after
+ // `maxTokenFailedAttempts` consecutive failures is the token evicted.
+ handleUnauthorized(token, pool) {
+ const failedAttempts = token.recordFailedAttempt()
+ if (failedAttempts >= this.maxTokenFailedAttempts) {
+ this.invalidateToken(token, 'http_401')
} else {
- return this.standardTokens.next()
+ log.log(
+ `GitHub token got a 401, rotating away and retrying later (attempt ${failedAttempts}/${this.maxTokenFailedAttempts}, token: ${sanitizeToken(
+ token.id,
+ )})`,
+ )
+ pool.endBatchFor(token)
}
}
- // Act like request(), but tweak headers and query to avoid hitting a rate
- // limit. Inject `request` so we can pass in `cachingRequest` from
- // `request-handler.js`.
- request(request, url, options = {}, callback) {
+ async fetch(requestFetcher, url, options = {}) {
const { baseUrl } = this
let token
let tokenString
- if (this.withPooling) {
+ let pool
+ if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {
+ pool = this.poolForUrl(url)
try {
- token = this.tokenForUrl(url)
+ token = pool.next()
} catch (e) {
- callback(e)
- return
+ log.error(e)
+ throw new ImproperlyConfigured({
+ prettyMessage: 'Unable to select next GitHub token from pool',
+ })
}
tokenString = token.id
- } else {
+ } else if (this.authType === this.constructor.AUTH_TYPES.GLOBAL_TOKEN) {
tokenString = this.globalToken
}
const mergedOptions = {
...options,
...{
- url,
- baseUrl,
headers: {
'User-Agent': userAgent,
- Accept: 'application/vnd.github.v3+json',
- Authorization: `token ${tokenString}`,
+ 'X-GitHub-Api-Version': this.restApiVersion,
+ ...options.headers,
},
},
}
+ if (
+ this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL ||
+ this.authType === this.constructor.AUTH_TYPES.GLOBAL_TOKEN
+ ) {
+ mergedOptions.headers.Authorization = `token ${tokenString}`
+ }
- request(mergedOptions, (err, res, buffer) => {
- if (err === null) {
- if (this.withPooling) {
- if (res.statusCode === 401) {
- this.invalidateToken(token)
- } else if (res.statusCode < 500) {
- this.updateToken({ token, url, res })
- }
- }
+ const response = await requestFetcher(`${baseUrl}${url}`, mergedOptions)
+ if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {
+ if (response.res.statusCode === 401) {
+ this.handleUnauthorized(token, pool)
+ } else if (response.res.statusCode < 500) {
+ // A non-401 response means the token authenticated successfully,
+ // so clear any earlier failed attempts.
+ token.resetFailedAttempts()
+ this.updateToken({ token, url, res: response.res })
}
- callback(err, res, buffer)
- })
- }
-
- requestAsPromise(request, url, options) {
- return new Promise((resolve, reject) => {
- this.request(request, url, options, (err, res, buffer) => {
- if (err) {
- reject(err)
- } else {
- resolve({ res, buffer })
- }
- })
- })
+ }
+ return response
}
}
-module.exports = GithubApiProvider
+export default GithubApiProvider
diff --git a/services/github/github-api-provider.spec.js b/services/github/github-api-provider.spec.js
index f67e788424c7b..9428b3798f70d 100644
--- a/services/github/github-api-provider.spec.js
+++ b/services/github/github-api-provider.spec.js
@@ -1,122 +1,123 @@
-'use strict'
+import { expect } from 'chai'
+import sinon from 'sinon'
+import GithubApiProvider from './github-api-provider.js'
-const { expect } = require('chai')
-const sinon = require('sinon')
-const GithubApiProvider = require('./github-api-provider')
-
-describe('Github API provider', function() {
+describe('Github API provider', function () {
const baseUrl = 'https://github-api.example.com'
const reserveFraction = 0.333
+ const maxTokenFailedAttempts = 3
+
+ // A stateful mock so recordFailedAttempt/resetFailedAttempts drive the
+ // provider's eviction threshold the same way the real Token would.
+ const makeMockToken = id => {
+ let failedAttempts = 0
+ return {
+ id,
+ update: sinon.spy(),
+ invalidate: sinon.spy(),
+ recordFailedAttempt: sinon.spy(() => (failedAttempts += 1)),
+ resetFailedAttempts: sinon.spy(() => {
+ failedAttempts = 0
+ }),
+ }
+ }
+
let mockStandardToken, mockSearchToken, mockGraphqlToken, provider
- beforeEach(function() {
- provider = new GithubApiProvider({ baseUrl, reserveFraction })
+ beforeEach(function () {
+ provider = new GithubApiProvider({
+ baseUrl,
+ authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL,
+ reserveFraction,
+ maxTokenFailedAttempts,
+ })
- mockStandardToken = { update: sinon.spy(), invalidate: sinon.spy() }
+ mockStandardToken = makeMockToken('standard-token')
sinon.stub(provider.standardTokens, 'next').returns(mockStandardToken)
- mockSearchToken = { update: sinon.spy(), invalidate: sinon.spy() }
+ mockSearchToken = makeMockToken('search-token')
sinon.stub(provider.searchTokens, 'next').returns(mockSearchToken)
- mockGraphqlToken = { update: sinon.spy(), invalidate: sinon.spy() }
+ mockGraphqlToken = makeMockToken('graphql-token')
sinon.stub(provider.graphqlTokens, 'next').returns(mockGraphqlToken)
})
- context('a search API request', function() {
- const mockRequest = (options, callback) => {
- callback()
- }
- it('should obtain an appropriate token', function(done) {
- provider.request(mockRequest, '/search', {}, (err, res, buffer) => {
- expect(err).to.be.undefined
- expect(provider.searchTokens.next).to.have.been.calledOnce
- expect(provider.standardTokens.next).not.to.have.been.called
- expect(provider.graphqlTokens.next).not.to.have.been.called
- done()
- })
+ context('a search API request', function () {
+ it('should obtain an appropriate token', async function () {
+ const mockResponse = { res: { headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ await provider.fetch(mockRequest, '/search', {})
+ expect(provider.searchTokens.next).to.have.been.calledOnce
+ expect(provider.standardTokens.next).not.to.have.been.called
+ expect(provider.graphqlTokens.next).not.to.have.been.called
})
})
- context('a graphql API request', function() {
- const mockRequest = (options, callback) => {
- callback()
- }
- it('should obtain an appropriate token', function(done) {
- provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
- expect(err).to.be.undefined
- expect(provider.searchTokens.next).not.to.have.been.called
- expect(provider.standardTokens.next).not.to.have.been.called
- expect(provider.graphqlTokens.next).to.have.been.calledOnce
- done()
- })
+ context('a graphql API request', function () {
+ it('should obtain an appropriate token', async function () {
+ const mockResponse = { res: { headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ await provider.fetch(mockRequest, '/graphql', {})
+ expect(provider.searchTokens.next).not.to.have.been.called
+ expect(provider.standardTokens.next).not.to.have.been.called
+ expect(provider.graphqlTokens.next).to.have.been.calledOnce
})
})
- context('a core API request', function() {
- const mockRequest = (options, callback) => {
- callback()
- }
- it('should obtain an appropriate token', function(done) {
- provider.request(mockRequest, '/repo', {}, (err, res, buffer) => {
- expect(err).to.be.undefined
- expect(provider.searchTokens.next).not.to.have.been.called
- expect(provider.standardTokens.next).to.have.been.calledOnce
- expect(provider.graphqlTokens.next).not.to.have.been.called
- done()
- })
+ context('a core API request', function () {
+ it('should obtain an appropriate token', async function () {
+ const mockResponse = { res: { headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ await provider.fetch(mockRequest, '/repo', {})
+ expect(provider.searchTokens.next).not.to.have.been.called
+ expect(provider.standardTokens.next).to.have.been.calledOnce
+ expect(provider.graphqlTokens.next).not.to.have.been.called
})
})
- context('a valid V3 API response', function() {
+ context('a valid V3 API response', function () {
const rateLimit = 12500
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
- statusCode: 200,
- headers: {
- 'x-ratelimit-limit': rateLimit,
- 'x-ratelimit-remaining': remaining,
- 'x-ratelimit-reset': nextReset,
+ res: {
+ statusCode: 200,
+ headers: {
+ 'x-ratelimit-limit': rateLimit,
+ 'x-ratelimit-remaining': remaining,
+ 'x-ratelimit-reset': nextReset,
+ },
+ buffer: Buffer.alloc(0),
},
}
- const mockBuffer = Buffer.alloc(0)
- const mockRequest = (...args) => {
- const callback = args.pop()
- callback(null, mockResponse, mockBuffer)
- }
+ const mockRequest = sinon.stub().resolves(mockResponse)
- it('should invoke the callback', function(done) {
- provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
- expect(err).to.equal(null)
- expect(Object.is(res, mockResponse)).to.be.true
- expect(Object.is(buffer, mockBuffer)).to.be.true
- done()
- })
+ it('should return the response', async function () {
+ const res = await provider.fetch(mockRequest, '/repo', {})
+ expect(Object.is(res, mockResponse)).to.be.true
})
- it('should update the token with the expected values', function(done) {
- provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
- expect(err).to.equal(null)
- const expectedUsesRemaining =
- remaining - Math.ceil(reserveFraction * rateLimit)
- expect(mockStandardToken.update).to.have.been.calledWith(
- expectedUsesRemaining,
- nextReset
- )
- expect(mockStandardToken.invalidate).not.to.have.been.called
- done()
- })
+ it('should update the token with the expected values', async function () {
+ await provider.fetch(mockRequest, '/foo', {})
+ const expectedUsesRemaining =
+ remaining - Math.ceil(reserveFraction * rateLimit)
+ expect(mockStandardToken.update).to.have.been.calledWith(
+ expectedUsesRemaining,
+ nextReset,
+ )
+ expect(mockStandardToken.invalidate).not.to.have.been.called
})
})
- context('a valid V4 API response', function() {
+ context('a valid V4 API response', function () {
const rateLimit = 12500
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
- statusCode: 200,
- headers: {},
- body: `{
+ res: {
+ statusCode: 200,
+ headers: {},
+ body: `{
"data": {
"rateLimit": {
"limit": 12500,
@@ -126,67 +127,96 @@ describe('Github API provider', function() {
}
}
}`,
+ },
}
- const mockBuffer = Buffer.alloc(0)
- const mockRequest = (...args) => {
- const callback = args.pop()
- callback(null, mockResponse, mockBuffer)
- }
+ const mockRequest = sinon.stub().resolves(mockResponse)
- it('should invoke the callback', function(done) {
- provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
- expect(err).to.equal(null)
- expect(Object.is(res, mockResponse)).to.be.true
- expect(Object.is(buffer, mockBuffer)).to.be.true
- done()
- })
+ it('should return the response', async function () {
+ const res = await provider.fetch(mockRequest, '/graphql', {})
+ expect(Object.is(res, mockResponse)).to.be.true
})
- it('should update the token with the expected values', function(done) {
- provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
- expect(err).to.equal(null)
- const expectedUsesRemaining =
- remaining - Math.ceil(reserveFraction * rateLimit)
- expect(mockGraphqlToken.update).to.have.been.calledWith(
- expectedUsesRemaining,
- nextReset
- )
- expect(mockGraphqlToken.invalidate).not.to.have.been.called
- done()
- })
+ it('should update the token with the expected values', async function () {
+ await provider.fetch(mockRequest, '/graphql', {})
+ const expectedUsesRemaining =
+ remaining - Math.ceil(reserveFraction * rateLimit)
+ expect(mockGraphqlToken.update).to.have.been.calledWith(
+ expectedUsesRemaining,
+ nextReset,
+ )
+ expect(mockGraphqlToken.invalidate).not.to.have.been.called
})
})
- context('an unauthorized response', function() {
- const mockResponse = { statusCode: 401 }
- const mockBuffer = Buffer.alloc(0)
- const mockRequest = (...args) => {
- const callback = args.pop()
- callback(null, mockResponse, mockBuffer)
- }
+ context('unauthorized API responses', function () {
+ it('does not evict the token on a single 401, but records the attempt (v3)', async function () {
+ const mockResponse = { res: { statusCode: 401, headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ await provider.fetch(mockRequest, '/foo', {})
+ expect(mockStandardToken.recordFailedAttempt).to.have.been.calledOnce
+ expect(mockStandardToken.invalidate).not.to.have.been.called
+ expect(mockStandardToken.update).not.to.have.been.called
+ })
- it('should invoke the callback and update the token with the expected values', function(done) {
- provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
- expect(err).to.equal(null)
- expect(mockStandardToken.invalidate).to.have.been.calledOnce
- expect(mockStandardToken.update).not.to.have.been.called
- done()
- })
+ it('evicts the token after maxTokenFailedAttempts consecutive 401s (v3)', async function () {
+ const mockResponse = { res: { statusCode: 401, headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ for (let i = 0; i < maxTokenFailedAttempts; i++) {
+ await provider.fetch(mockRequest, '/foo', {})
+ }
+ expect(mockStandardToken.invalidate).to.have.been.calledOnce
})
- })
- context('a connection error', function() {
- const mockRequest = (...args) => {
- const callback = args.pop()
- callback(Error('connection timeout'))
- }
+ it('evicts the token after maxTokenFailedAttempts consecutive 401s (v4)', async function () {
+ const mockResponse = { res: { statusCode: 401, body: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ for (let i = 0; i < maxTokenFailedAttempts; i++) {
+ await provider.fetch(mockRequest, '/graphql', {})
+ }
+ expect(mockGraphqlToken.invalidate).to.have.been.calledOnce
+ expect(mockGraphqlToken.update).not.to.have.been.called
+ })
+
+ it('resets the failed-attempt counter on a successful response (v3)', async function () {
+ const failResponse = { res: { statusCode: 401, headers: {} } }
+ const okResponse = {
+ res: {
+ statusCode: 200,
+ headers: {
+ 'x-ratelimit-limit': 5000,
+ 'x-ratelimit-remaining': 4000,
+ 'x-ratelimit-reset': 123456789,
+ },
+ },
+ }
+ await provider.fetch(sinon.stub().resolves(failResponse), '/foo', {})
+ await provider.fetch(sinon.stub().resolves(okResponse), '/foo', {})
+ expect(mockStandardToken.resetFailedAttempts).to.have.been.calledOnce
+ })
+
+ it('invalidates immediately when the account is suspended (v4)', async function () {
+ const mockResponse = {
+ res: {
+ statusCode: 200,
+ body: '{ "message": "Sorry. Your account was suspended." }',
+ },
+ }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ await provider.fetch(mockRequest, '/graphql', {})
+ expect(mockGraphqlToken.invalidate).to.have.been.calledOnce
+ expect(mockGraphqlToken.update).not.to.have.been.called
+ })
+ })
- it('should pass the error to the callback', function(done) {
- provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
- expect(err).to.be.an.instanceof(Error)
- expect(err.message).to.equal('connection timeout')
- done()
- })
+ context('a connection error', function () {
+ it('should throw an exception', function () {
+ const msg = 'connection timeout'
+ const requestError = new Error(msg)
+ const mockRequest = sinon.stub().rejects(requestError)
+ return expect(provider.fetch(mockRequest, '/foo', {})).to.be.rejectedWith(
+ Error,
+ 'connection timeout',
+ )
})
})
})
diff --git a/services/github/github-auth-service.js b/services/github/github-auth-service.js
index e897509c11e77..67f249f02c9e5 100644
--- a/services/github/github-auth-service.js
+++ b/services/github/github-auth-service.js
@@ -1,25 +1,16 @@
-'use strict'
+import gql from 'graphql-tag'
+import { mergeQueries } from '../../core/base-service/graphql.js'
+import { BaseGraphqlService, BaseJsonService } from '../index.js'
-const gql = require('graphql-tag')
-const { mergeQueries } = require('../../core/base-service/graphql')
-const { BaseGraphqlService, BaseJsonService } = require('..')
-const { staticAuthConfigured } = require('./github-helpers')
-
-function createRequestFetcher(context, config) {
- const { sendAndCacheRequestWithCallbacks, githubApiProvider } = context
-
- return async (url, options) =>
- githubApiProvider.requestAsPromise(
- sendAndCacheRequestWithCallbacks,
- url,
- options
- )
+function createRequestFetcher(context) {
+ const { requestFetcher, githubApiProvider } = context
+ return githubApiProvider.fetch.bind(githubApiProvider, requestFetcher)
}
class GithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
super(context, config)
- this._requestFetcher = createRequestFetcher(context, config)
+ this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
}
}
@@ -32,8 +23,8 @@ class GithubAuthV3Service extends BaseJsonService {
class ConditionalGithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
super(context, config)
- if (staticAuthConfigured()) {
- this._requestFetcher = createRequestFetcher(context, config)
+ if (context.githubApiProvider.globalToken) {
+ this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
} else {
this.staticAuthConfigured = false
@@ -44,12 +35,12 @@ class ConditionalGithubAuthV3Service extends BaseJsonService {
class GithubAuthV4Service extends BaseGraphqlService {
constructor(context, config) {
super(context, config)
- this._requestFetcher = createRequestFetcher(context, config)
+ this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
}
async _requestGraphql(attrs) {
- const url = `/graphql`
+ const url = '/graphql'
/*
The Github v4 API requires us to query the rateLimit object to return
@@ -69,10 +60,17 @@ class GithubAuthV4Service extends BaseGraphqlService {
resetAt
}
}
- `
+ `,
)
- return super._requestGraphql({ ...attrs, ...{ url, query } })
+ return super._requestGraphql({
+ ...attrs,
+ ...{
+ url,
+ query,
+ httpErrorMessages: { 401: 'auth required for graphql api' },
+ },
+ })
}
}
@@ -90,7 +88,7 @@ All other things being equal, a graphql query will almost always be a smaller
number of bytes over the wire and a smaller/simpler object to parse.
*/
-module.exports = {
+export {
GithubAuthV3Service,
ConditionalGithubAuthV3Service,
GithubAuthV4Service,
diff --git a/services/github/github-auth-service.spec.js b/services/github/github-auth-service.spec.js
new file mode 100644
index 0000000000000..bda6a49931260
--- /dev/null
+++ b/services/github/github-auth-service.spec.js
@@ -0,0 +1,72 @@
+import Joi from 'joi'
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import GithubApiProvider from './github-api-provider.js'
+
+describe('GithubAuthV3Service', function () {
+ class DummyGithubAuthV3Service extends GithubAuthV3Service {
+ static category = 'build'
+ static route = { base: 'runs' }
+
+ async handle() {
+ const { requiredString } = await this._requestJson({
+ schema: Joi.object({
+ requiredString: Joi.string().required(),
+ }).required(),
+ url: '/repos/badges/shields/check-runs',
+ options: {
+ headers: {
+ Accept: 'application/vnd.github.antiope-preview+json',
+ },
+ },
+ })
+ return { message: requiredString }
+ }
+ }
+
+ it('forwards custom Accept header', async function () {
+ const requestFetcher = sinon.stub().returns(
+ Promise.resolve({
+ buffer: '{"requiredString": "some-string"}',
+ res: {
+ statusCode: 200,
+ headers: {
+ 'x-ratelimit-limit': 12500,
+ 'x-ratelimit-remaining': 7955,
+ 'x-ratelimit-reset': 123456789,
+ },
+ },
+ }),
+ )
+ const githubApiProvider = new GithubApiProvider({
+ baseUrl: 'https://github-api.example.com',
+ authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL,
+ restApiVersion: '2022-11-28',
+ })
+ const mockToken = {
+ update: sinon.mock(),
+ invalidate: sinon.mock(),
+ recordFailedAttempt: sinon.mock(),
+ resetFailedAttempts: sinon.mock(),
+ }
+ sinon.stub(githubApiProvider.standardTokens, 'next').returns(mockToken)
+
+ DummyGithubAuthV3Service.invoke({
+ requestFetcher,
+ githubApiProvider,
+ })
+
+ expect(requestFetcher).to.have.been.calledOnceWith(
+ 'https://github-api.example.com/repos/badges/shields/check-runs',
+ {
+ headers: {
+ 'User-Agent': 'shields (self-hosted)/dev',
+ Accept: 'application/vnd.github.antiope-preview+json',
+ Authorization: 'token undefined',
+ 'X-GitHub-Api-Version': '2022-11-28',
+ },
+ },
+ )
+ })
+})
diff --git a/services/github/github-branch-count.service.js b/services/github/github-branch-count.service.js
new file mode 100644
index 0000000000000..bf00e5cb18946
--- /dev/null
+++ b/services/github/github-branch-count.service.js
@@ -0,0 +1,72 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { transformErrors, documentation } from './github-helpers.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ refs: Joi.object({
+ totalCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
+
+export default class GithubBranchCount extends GithubAuthV4Service {
+ static category = 'analysis'
+
+ static route = {
+ base: 'github/branches',
+ pattern: ':user/:repo',
+ }
+
+ static openApi = {
+ '/github/branches/{user}/{repo}': {
+ get: {
+ summary: 'GitHub branch count',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'branches', color: 'blue' }
+
+ static render({ count }) {
+ return {
+ label: 'branches',
+ message: metric(count),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ user, repo }) {
+ return this._requestGraphql({
+ query: gql`
+ query ($user: String!, $repo: String!) {
+ repository(owner: $user, name: $repo) {
+ refs(refPrefix: "refs/heads/", first: 1) {
+ totalCount
+ }
+ }
+ }
+ `,
+ variables: { user, repo },
+ schema,
+ transformErrors,
+ })
+ }
+
+ async handle({ user, repo }) {
+ const json = await this.fetch({ user, repo })
+ const count = json.data.repository.refs.totalCount
+ return this.constructor.render({ count })
+ }
+}
diff --git a/services/github/github-branch-count.tester.js b/services/github/github-branch-count.tester.js
new file mode 100644
index 0000000000000..edea1fb788f81
--- /dev/null
+++ b/services/github/github-branch-count.tester.js
@@ -0,0 +1,17 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('branch count').get('/badges/shields.json').expectBadge({
+ label: 'branches',
+ message: isMetric,
+ color: 'blue',
+})
+
+t.create('repo not found')
+ .get('/badges/shields-nonexistent-repo-xyz.json')
+ .expectBadge({
+ label: 'branches',
+ message: 'repo not found',
+ })
diff --git a/services/github/github-check-runs.service.js b/services/github/github-check-runs.service.js
new file mode 100644
index 0000000000000..edf08a9944184
--- /dev/null
+++ b/services/github/github-check-runs.service.js
@@ -0,0 +1,195 @@
+import Joi from 'joi'
+import countBy from 'lodash.countby'
+import { pathParam, queryParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
+ documentation as commonDocumentation,
+ httpErrorsFor,
+} from './github-helpers.js'
+
+const description = `
+The Check Runs service shows the status of GitHub action runs.
+
+For an aggregate status that follows the parent check suite result, use the
+corresponding [Check Suites badge][check-suites-link] instead.
+
+${commonDocumentation}
+`
+
+const checkStatuses = [
+ 'completed',
+ 'in_progress',
+ 'pending',
+ 'queued',
+ 'requested',
+ 'waiting',
+]
+
+const checkRunConclusions = [
+ 'action_required',
+ 'cancelled',
+ 'failure',
+ 'neutral',
+ 'skipped',
+ 'stale',
+ 'startup_failure',
+ 'success',
+ 'timed_out',
+]
+
+const schema = Joi.object({
+ total_count: nonNegativeInteger,
+ check_runs: Joi.array()
+ .items(
+ Joi.object({
+ name: Joi.string().required(),
+ status: Joi.equal(...checkStatuses).required(),
+ conclusion: Joi.equal(...checkRunConclusions, null).required(),
+ }),
+ )
+ .default([]),
+}).required()
+
+const queryParamSchema = Joi.object({
+ nameFilter: Joi.string(),
+})
+
+export default class GithubCheckRuns extends GithubAuthV3Service {
+ static category = 'build'
+ static route = {
+ base: 'github/check-runs',
+ pattern: ':user/:repo/:ref+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/check-runs/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub branch check runs',
+ description: `${description}
+ [check-suites-link]: https://shields.io/badges/git-hub-branch-check-suites`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'nameFilter',
+ description: 'Name of a check run',
+ example: 'test-lint',
+ required: true,
+ }),
+ ],
+ },
+ },
+ '/github/check-runs/{user}/{repo}/{commit}': {
+ get: {
+ summary: 'GitHub commit check runs',
+ description: `${description}
+ [check-suites-link]: https://shields.io/badges/git-hub-commit-check-suites`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({
+ name: 'commit',
+ example: '91b108d4b7359b2f8794a4614c11cb1157dc9fff',
+ }),
+ queryParam({
+ name: 'nameFilter',
+ description: 'Name of a check run',
+ example: 'test-lint',
+ required: true,
+ }),
+ ],
+ },
+ },
+ '/github/check-runs/{user}/{repo}/{tag}': {
+ get: {
+ summary: 'GitHub tag check runs',
+ description: `${description}
+ [check-suites-link]: https://shields.io/badges/git-hub-tag-check-suites`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'tag', example: '3.3.0' }),
+ queryParam({
+ name: 'nameFilter',
+ description: 'Name of a check run',
+ example: 'test-lint',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'checks' }
+
+ static transform(
+ { total_count: totalCount, check_runs: checkRuns },
+ nameFilter,
+ ) {
+ const filteredCheckRuns =
+ nameFilter && nameFilter.length > 0
+ ? checkRuns.filter(checkRun => checkRun.name === nameFilter)
+ : checkRuns
+
+ return {
+ total:
+ nameFilter && nameFilter.length > 0
+ ? filteredCheckRuns.length
+ : totalCount,
+ statusCounts: countBy(filteredCheckRuns, 'status'),
+ conclusionCounts: countBy(filteredCheckRuns, 'conclusion'),
+ }
+ }
+
+ static mapState({ total, statusCounts, conclusionCounts }) {
+ let state
+ if (total === 0) {
+ state = 'no check runs'
+ } else if (statusCounts.queued) {
+ state = 'queued'
+ } else if (statusCounts.requested || statusCounts.waiting) {
+ state = 'queued'
+ } else if (statusCounts.in_progress || statusCounts.pending) {
+ state = 'pending'
+ } else if (statusCounts.completed) {
+ // all check runs are completed, now evaluate conclusions
+ const orangeStates = ['action_required', 'stale']
+ const redStates = ['cancelled', 'failure', 'startup_failure', 'timed_out']
+
+ // assume "passing (green)"
+ state = 'passing'
+ for (const stateValue of Object.keys(conclusionCounts)) {
+ if (orangeStates.includes(stateValue)) {
+ // orange state renders "passing (orange)"
+ state = 'partially succeeded'
+ } else if (redStates.includes(stateValue)) {
+ // red state renders "failing (red)"
+ state = 'failing'
+ break
+ }
+ }
+ } else {
+ state = 'unknown status'
+ }
+ return state
+ }
+
+ async handle({ user, repo, ref }, { nameFilter }) {
+ // https://docs.github.com/en/rest/checks/runs#list-check-runs-for-a-git-reference
+ const json = await this._requestJson({
+ url: `/repos/${user}/${repo}/commits/${ref}/check-runs`,
+ httpErrors: httpErrorsFor('ref or repo not found'),
+ schema,
+ })
+
+ const state = this.constructor.mapState(
+ this.constructor.transform(json, nameFilter),
+ )
+
+ return renderBuildStatusBadge({ status: state })
+ }
+}
diff --git a/services/github/github-check-runs.spec.js b/services/github/github-check-runs.spec.js
new file mode 100644
index 0000000000000..ed1f9f12e02ac
--- /dev/null
+++ b/services/github/github-check-runs.spec.js
@@ -0,0 +1,120 @@
+import { test, given } from 'sazerac'
+import GithubCheckRuns from './github-check-runs.service.js'
+
+describe('GithubCheckRuns.transform', function () {
+ test(GithubCheckRuns.transform, () => {
+ given(
+ {
+ total_count: 3,
+ check_runs: [
+ { status: 'completed', conclusion: 'success' },
+ { status: 'completed', conclusion: 'failure' },
+ { status: 'in_progress', conclusion: null },
+ ],
+ },
+ '',
+ ).expect({
+ total: 3,
+ statusCounts: { completed: 2, in_progress: 1 },
+ conclusionCounts: { success: 1, failure: 1, null: 1 },
+ })
+
+ given(
+ {
+ total_count: 3,
+ check_runs: [
+ { name: 'test1', status: 'completed', conclusion: 'success' },
+ { name: 'test2', status: 'completed', conclusion: 'failure' },
+ { name: 'test3', status: 'in_progress', conclusion: null },
+ ],
+ },
+ '',
+ ).expect({
+ total: 3,
+ statusCounts: { completed: 2, in_progress: 1 },
+ conclusionCounts: { success: 1, failure: 1, null: 1 },
+ })
+
+ given(
+ {
+ total_count: 3,
+ check_runs: [
+ { name: 'test1', status: 'completed', conclusion: 'success' },
+ { name: 'test2', status: 'completed', conclusion: 'failure' },
+ { name: 'test3', status: 'in_progress', conclusion: null },
+ ],
+ },
+ 'test1',
+ ).expect({
+ total: 1,
+ statusCounts: { completed: 1 },
+ conclusionCounts: { success: 1 },
+ })
+
+ given(
+ {
+ total_count: 3,
+ check_runs: [
+ { name: 'test1', status: 'completed', conclusion: 'success' },
+ { name: 'test2', status: 'completed', conclusion: 'failure' },
+ { name: 'test3', status: 'in_progress', conclusion: null },
+ ],
+ },
+ 'missing',
+ ).expect({
+ total: 0,
+ statusCounts: {},
+ conclusionCounts: {},
+ })
+ })
+})
+
+describe('GithubCheckRuns', function () {
+ test(GithubCheckRuns.mapState, () => {
+ given({
+ total: 0,
+ statusCounts: null,
+ conclusionCounts: null,
+ }).expect('no check runs')
+ given({
+ total: 1,
+ statusCounts: { queued: 1 },
+ conclusionCounts: null,
+ }).expect('queued')
+ given({
+ total: 1,
+ statusCounts: { in_progress: 1 },
+ conclusionCounts: null,
+ }).expect('pending')
+ given({
+ total: 1,
+ statusCounts: { waiting: 1 },
+ conclusionCounts: null,
+ }).expect('queued')
+ given({
+ total: 1,
+ statusCounts: { pending: 1 },
+ conclusionCounts: null,
+ }).expect('pending')
+ given({
+ total: 1,
+ statusCounts: { completed: 1 },
+ conclusionCounts: { success: 1 },
+ }).expect('passing')
+ given({
+ total: 2,
+ statusCounts: { completed: 2 },
+ conclusionCounts: { success: 1, stale: 1 },
+ }).expect('partially succeeded')
+ given({
+ total: 3,
+ statusCounts: { completed: 3 },
+ conclusionCounts: { success: 1, stale: 1, failure: 1 },
+ }).expect('failing')
+ given({
+ total: 1,
+ statusCounts: { somethingelse: 1 },
+ conclusionCounts: null,
+ }).expect('unknown status')
+ })
+})
diff --git a/services/github/github-check-runs.tester.js b/services/github/github-check-runs.tester.js
new file mode 100644
index 0000000000000..a1cbb570084c9
--- /dev/null
+++ b/services/github/github-check-runs.tester.js
@@ -0,0 +1,32 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { isBuildStatus } from '../build-status.js'
+export const t = await createServiceTester()
+
+t.create('check runs - for branch')
+ .get('/badges/shields/master.json')
+ .expectBadge({
+ label: 'checks',
+ message: isBuildStatus,
+ })
+
+t.create('check runs - for branch with filter')
+ .get('/badges/shields/master.json?nameFilter=test-lint')
+ .expectBadge({
+ label: 'checks',
+ message: Joi.alternatives().try(isBuildStatus, Joi.equal('no check runs')),
+ })
+
+t.create('check runs - no tests')
+ .get('/badges/shields/5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c.json')
+ .expectBadge({
+ label: 'checks',
+ message: 'no check runs',
+ })
+
+t.create('check runs - nonexistent ref')
+ .get('/badges/shields/this-ref-does-not-exist.json')
+ .expectBadge({
+ label: 'checks',
+ message: 'ref or repo not found',
+ })
diff --git a/services/github/github-check-suites.service.js b/services/github/github-check-suites.service.js
new file mode 100644
index 0000000000000..55155121e5710
--- /dev/null
+++ b/services/github/github-check-suites.service.js
@@ -0,0 +1,162 @@
+import Joi from 'joi'
+import countBy from 'lodash.countby'
+import { pathParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
+ documentation as commonDocumentation,
+ httpErrorsFor,
+} from './github-helpers.js'
+
+const description = `
+The Check Suites service shows the aggregate status of GitHub check suites.
+This is useful for GitHub Actions workflows where individual check runs can fail
+with continue-on-error while the parent check suite succeeds.
+
+For individual check run status, use the corresponding
+[Check Runs badge][check-runs-link] instead.
+
+${commonDocumentation}
+`
+
+const checkStatuses = [
+ 'completed',
+ 'in_progress',
+ 'pending',
+ 'queued',
+ 'requested',
+ 'waiting',
+]
+
+const checkSuiteConclusions = [
+ 'action_required',
+ 'cancelled',
+ 'failure',
+ 'neutral',
+ 'skipped',
+ 'stale',
+ 'startup_failure',
+ 'success',
+ 'timed_out',
+]
+
+const schema = Joi.object({
+ check_suites: Joi.array()
+ .items(
+ Joi.object({
+ status: Joi.equal(...checkStatuses).required(),
+ conclusion: Joi.equal(...checkSuiteConclusions, null).required(),
+ latest_check_runs_count: nonNegativeInteger.required(),
+ }),
+ )
+ .default([]),
+}).required()
+
+export default class GithubCheckSuites extends GithubAuthV3Service {
+ static category = 'build'
+ static route = {
+ base: 'github/check-suites',
+ pattern: ':user/:repo/:ref+',
+ }
+
+ static openApi = {
+ '/github/check-suites/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub branch check suites',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-branch-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ ],
+ },
+ },
+ '/github/check-suites/{user}/{repo}/{commit}': {
+ get: {
+ summary: 'GitHub commit check suites',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-commit-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({
+ name: 'commit',
+ example: '91b108d4b7359b2f8794a4614c11cb1157dc9fff',
+ }),
+ ],
+ },
+ },
+ '/github/check-suites/{user}/{repo}/{tag}': {
+ get: {
+ summary: 'GitHub tag check suites',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-tag-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'tag', example: '3.3.0' }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'checks' }
+
+ static transform({ check_suites: checkSuites }) {
+ const suitesWithCheckRuns = checkSuites.filter(
+ checkSuite => checkSuite.latest_check_runs_count > 0,
+ )
+
+ return {
+ total: suitesWithCheckRuns.length,
+ statusCounts: countBy(suitesWithCheckRuns, 'status'),
+ conclusionCounts: countBy(suitesWithCheckRuns, 'conclusion'),
+ }
+ }
+
+ static mapState({ total, statusCounts, conclusionCounts }) {
+ let state
+ if (total === 0) {
+ state = 'no check suites'
+ } else if (
+ statusCounts.queued ||
+ statusCounts.requested ||
+ statusCounts.waiting
+ ) {
+ state = 'queued'
+ } else if (statusCounts.in_progress || statusCounts.pending) {
+ state = 'pending'
+ } else if (statusCounts.completed) {
+ const orangeStates = ['action_required', 'stale']
+ const redStates = ['cancelled', 'failure', 'startup_failure', 'timed_out']
+
+ state = 'passing'
+ for (const stateValue of Object.keys(conclusionCounts)) {
+ if (orangeStates.includes(stateValue)) {
+ state = 'partially succeeded'
+ } else if (redStates.includes(stateValue)) {
+ state = 'failing'
+ break
+ }
+ }
+ } else {
+ state = 'unknown status'
+ }
+ return state
+ }
+
+ async handle({ user, repo, ref }) {
+ // https://docs.github.com/en/rest/checks/suites#list-check-suites-for-a-git-reference
+ const json = await this._requestJson({
+ url: `/repos/${user}/${repo}/commits/${ref}/check-suites`,
+ httpErrors: httpErrorsFor('ref or repo not found'),
+ schema,
+ })
+
+ const state = this.constructor.mapState(this.constructor.transform(json))
+
+ return renderBuildStatusBadge({ status: state })
+ }
+}
diff --git a/services/github/github-check-suites.spec.js b/services/github/github-check-suites.spec.js
new file mode 100644
index 0000000000000..0a57765c469cb
--- /dev/null
+++ b/services/github/github-check-suites.spec.js
@@ -0,0 +1,85 @@
+import { test, given } from 'sazerac'
+import GithubCheckSuites from './github-check-suites.service.js'
+
+describe('GithubCheckSuites.transform', function () {
+ test(GithubCheckSuites.transform, () => {
+ given({
+ check_suites: [
+ {
+ status: 'completed',
+ conclusion: 'success',
+ latest_check_runs_count: 27,
+ },
+ {
+ status: 'completed',
+ conclusion: 'success',
+ latest_check_runs_count: 6,
+ },
+ {
+ status: 'queued',
+ conclusion: null,
+ latest_check_runs_count: 0,
+ },
+ ],
+ }).expect({
+ total: 2,
+ statusCounts: { completed: 2 },
+ conclusionCounts: { success: 2 },
+ })
+ })
+})
+
+describe('GithubCheckSuites', function () {
+ test(GithubCheckSuites.mapState, () => {
+ given({
+ total: 0,
+ statusCounts: null,
+ conclusionCounts: null,
+ }).expect('no check suites')
+ given({
+ total: 1,
+ statusCounts: { queued: 1 },
+ conclusionCounts: null,
+ }).expect('queued')
+ given({
+ total: 1,
+ statusCounts: { requested: 1 },
+ conclusionCounts: null,
+ }).expect('queued')
+ given({
+ total: 1,
+ statusCounts: { waiting: 1 },
+ conclusionCounts: null,
+ }).expect('queued')
+ given({
+ total: 1,
+ statusCounts: { in_progress: 1 },
+ conclusionCounts: null,
+ }).expect('pending')
+ given({
+ total: 1,
+ statusCounts: { pending: 1 },
+ conclusionCounts: null,
+ }).expect('pending')
+ given({
+ total: 1,
+ statusCounts: { completed: 1 },
+ conclusionCounts: { success: 1 },
+ }).expect('passing')
+ given({
+ total: 2,
+ statusCounts: { completed: 2 },
+ conclusionCounts: { success: 1, stale: 1 },
+ }).expect('partially succeeded')
+ given({
+ total: 3,
+ statusCounts: { completed: 3 },
+ conclusionCounts: { success: 1, stale: 1, startup_failure: 1 },
+ }).expect('failing')
+ given({
+ total: 1,
+ statusCounts: { somethingelse: 1 },
+ conclusionCounts: null,
+ }).expect('unknown status')
+ })
+})
diff --git a/services/github/github-check-suites.tester.js b/services/github/github-check-suites.tester.js
new file mode 100644
index 0000000000000..64fbc5ed6fe66
--- /dev/null
+++ b/services/github/github-check-suites.tester.js
@@ -0,0 +1,24 @@
+import { createServiceTester } from '../tester.js'
+import { isBuildStatus } from '../build-status.js'
+export const t = await createServiceTester()
+
+t.create('check suites - for branch')
+ .get('/badges/shields/master.json')
+ .expectBadge({
+ label: 'checks',
+ message: isBuildStatus,
+ })
+
+t.create('check suites - no tests')
+ .get('/badges/shields/5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c.json')
+ .expectBadge({
+ label: 'checks',
+ message: 'no check suites',
+ })
+
+t.create('check suites - nonexistent ref')
+ .get('/badges/shields/this-ref-does-not-exist.json')
+ .expectBadge({
+ label: 'checks',
+ message: 'ref or repo not found',
+ })
diff --git a/services/github/github-checks-status.service.js b/services/github/github-checks-status.service.js
new file mode 100644
index 0000000000000..4deac27cd6ed4
--- /dev/null
+++ b/services/github/github-checks-status.service.js
@@ -0,0 +1,85 @@
+import Joi from 'joi'
+import { pathParam } from '../index.js'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
+ documentation as commonDocumentation,
+ httpErrorsFor,
+} from './github-helpers.js'
+
+const description = `
+Displays the status of a tag, commit, or branch, as reported by the Commit Status API.
+
+Note: Nowadays, GitHub Actions and many third party integrations report state via
+the Checks API. If this badge does not show expected values, please try out our
+corresponding [Check Runs badge][check-runs-link] instead. You can read more about status checks in
+the [GitHub documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks).
+
+${commonDocumentation}
+`
+
+const schema = Joi.object({
+ state: isBuildStatus,
+}).required()
+
+export default class GithubChecksStatus extends GithubAuthV3Service {
+ static category = 'build'
+ static route = {
+ base: 'github/checks-status',
+ pattern: ':user/:repo/:ref',
+ }
+
+ static openApi = {
+ '/github/checks-status/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub branch status',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-branch-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ ],
+ },
+ },
+ '/github/checks-status/{user}/{repo}/{commit}': {
+ get: {
+ summary: 'GitHub commit status',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-commit-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({
+ name: 'commit',
+ example: '91b108d4b7359b2f8794a4614c11cb1157dc9fff',
+ }),
+ ],
+ },
+ },
+ '/github/checks-status/{user}/{repo}/{tag}': {
+ get: {
+ summary: 'GitHub tag status',
+ description: `${description}
+ [check-runs-link]: https://shields.io/badges/git-hub-tag-check-runs`,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'tag', example: '3.3.0' }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'checks' }
+
+ async handle({ user, repo, ref }) {
+ const { state } = await this._requestJson({
+ url: `/repos/${user}/${repo}/commits/${ref}/status`,
+ httpErrors: httpErrorsFor('ref or repo not found'),
+ schema,
+ })
+
+ return renderBuildStatusBadge({ status: state })
+ }
+}
diff --git a/services/github/github-checks-status.tester.js b/services/github/github-checks-status.tester.js
new file mode 100644
index 0000000000000..80cfe34bdd3ac
--- /dev/null
+++ b/services/github/github-checks-status.tester.js
@@ -0,0 +1,18 @@
+import { createServiceTester } from '../tester.js'
+import { isBuildStatus } from '../build-status.js'
+export const t = await createServiceTester()
+
+t.create('branch checks (branch)')
+ .get('/badges/shields/master.json')
+ .expectBadge({
+ label: 'checks',
+ message: isBuildStatus,
+ })
+
+t.create('checks - nonexistent ref')
+ .get('/badges/shields/this-ref-does-not-exist.json')
+ .expectBadge({
+ label: 'checks',
+ message: 'ref or repo not found',
+ color: 'red',
+ })
diff --git a/services/github/github-code-size.service.js b/services/github/github-code-size.service.js
index 5046a16f32941..7ade29503099f 100644
--- a/services/github/github-code-size.service.js
+++ b/services/github/github-code-size.service.js
@@ -1,50 +1,38 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderSizeBadge } from '../size.js'
+import { BaseGithubLanguage } from './github-languages-base.js'
+import { documentation } from './github-helpers.js'
-const prettyBytes = require('pretty-bytes')
-const { BaseGithubLanguage } = require('./github-languages-base')
-const { documentation } = require('./github-helpers')
-
-module.exports = class GithubCodeSize extends BaseGithubLanguage {
- static get category() {
- return 'size'
- }
-
- static get route() {
- return {
- base: 'github/languages/code-size',
- pattern: ':user/:repo',
- }
+export default class GithubCodeSize extends BaseGithubLanguage {
+ static category = 'size'
+ static route = {
+ base: 'github/languages/code-size',
+ pattern: ':user/:repo',
}
- static get examples() {
- return [
- {
- title: 'GitHub code size in bytes',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: this.render({ size: 1625000 }),
- documentation,
+ static openApi = {
+ '/github/languages/code-size/{user}/{repo}': {
+ get: {
+ summary: 'GitHub code size in bytes',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'code size',
- }
- }
-
- static render({ size }) {
- return {
- message: prettyBytes(size),
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'code size' }
async handle({ user, repo }) {
const data = await this.fetch({ user, repo })
- return this.constructor.render({ size: this.getTotalSize(data) })
+ return renderSizeBadge(this.getTotalSize(data), 'iec', 'code size')
}
}
diff --git a/services/github/github-code-size.tester.js b/services/github/github-code-size.tester.js
index 8a6e52e13c63c..16c41129d798f 100644
--- a/services/github/github-code-size.tester.js
+++ b/services/github/github-code-size.tester.js
@@ -1,13 +1,12 @@
-'use strict'
-
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIecFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('code size in bytes for all languages')
.get('/badges/shields.json')
.expectBadge({
label: 'code size',
- message: isFileSize,
+ message: isIecFileSize,
})
t.create('code size in bytes for all languages (empty repo)')
diff --git a/services/github/github-commit-activity.service.js b/services/github/github-commit-activity.service.js
index 5e33bf84fc67a..f1504f36387b7 100644
--- a/services/github/github-commit-activity.service.js
+++ b/services/github/github-commit-activity.service.js
@@ -1,54 +1,95 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { errorMessagesFor, documentation } = require('./github-helpers')
-
-const schema = Joi.array()
- .items(
- Joi.object({
- total: nonNegativeInteger,
- })
- )
- .required()
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import parseLinkHeader from 'parse-link-header'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import {
+ transformErrors,
+ documentation,
+ httpErrorsFor,
+} from './github-helpers.js'
-module.exports = class GithubCommitActivity extends GithubAuthV3Service {
- static get category() {
- return 'activity'
- }
+const schema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ object: Joi.object({
+ history: Joi.object({
+ totalCount: nonNegativeInteger,
+ }).required(),
+ }).allow(null),
+ }).required(),
+ }).required(),
+}).required()
- static get route() {
- return {
- base: 'github/commit-activity',
- pattern: ':interval(y|m|4w|w)/:user/:repo',
- }
+const queryParamSchema = Joi.object({
+ authorFilter: Joi.string(),
+})
+
+export default class GitHubCommitActivity extends GithubAuthV4Service {
+ static category = 'activity'
+ static route = {
+ base: 'github/commit-activity',
+ pattern: ':interval(t|y|m|4w|w)/:user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub commit activity',
- // Override the pattern to omit the deprecated interval "4w".
- pattern: ':interval(y|m|w)/:user/:repo',
- namedParams: { interval: 'm', user: 'eslint', repo: 'eslint' },
- staticPreview: this.render({ interval: 'm', commitCount: 457 }),
- keywords: ['commits'],
- documentation,
+ static openApi = {
+ '/github/commit-activity/{interval}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub commit activity',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'interval',
+ example: 'm',
+ description: 'Commits in the last Week, Month, Year, or Total',
+ schema: {
+ type: 'string',
+ // Override the enum to omit the deprecated interval "4w".
+ enum: ['w', 'm', 'y', 't'],
+ },
+ }),
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'squint' }),
+ queryParam({ name: 'authorFilter', example: 'calebcartwright' }),
+ ],
+ },
+ },
+ '/github/commit-activity/{interval}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub commit activity (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'interval',
+ example: 'm',
+ description: 'Commits in the last Week, Month, Year, or Total',
+ schema: {
+ type: 'string',
+ // Override the enum to omit the deprecated interval "4w".
+ enum: ['w', 'm', 'y', 't'],
+ },
+ }),
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'squint' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ queryParam({ name: 'authorFilter', example: 'calebcartwright' }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'commit activity',
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'commit activity', color: 'blue' }
+
+ static render({ interval, commitCount, authorFilter }) {
+ // If total commits selected change label from commit activity to commits
+ const label = interval === 't' ? 'commits' : 'commit activity'
+ const authorFilterLabel = authorFilter ? ` by ${authorFilter}` : ''
- static render({ interval, commitCount }) {
const intervalLabel = {
+ t: '',
y: '/year',
m: '/month',
'4w': '/four weeks',
@@ -56,51 +97,120 @@ module.exports = class GithubCommitActivity extends GithubAuthV3Service {
}[interval]
return {
+ label: `${label}${authorFilterLabel}`,
message: `${metric(commitCount)}${intervalLabel}`,
}
}
- static transform({ interval, weekData }) {
- const weekTotals = weekData.map(({ total }) => total)
-
- if (interval === 'm') {
- // To approximate the value for the past month, get the sum for the last
- // four weeks and add a weighted value for the fifth week.
- const fourWeeksValue = weekTotals
- .slice(-4)
- .reduce((sum, weekTotal) => sum + weekTotal, 0)
- const fifthWeekValue = weekTotals.slice(-5)[0]
- const averageWeeksPerMonth = 365 / 12 / 7
- return (
- fourWeeksValue + Math.round((averageWeeksPerMonth - 4) * fifthWeekValue)
- )
+ async fetch({ interval, user, repo, branch = 'HEAD' }) {
+ const since = this.constructor.getIntervalQueryStartDate({ interval })
+ return this._requestGraphql({
+ query: gql`
+ query (
+ $user: String!
+ $repo: String!
+ $branch: String!
+ $since: GitTimestamp
+ ) {
+ repository(owner: $user, name: $repo) {
+ object(expression: $branch) {
+ ... on Commit {
+ history(since: $since) {
+ totalCount
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ user,
+ repo,
+ branch,
+ since,
+ },
+ schema,
+ transformErrors,
+ })
+ }
+
+ async fetchAuthorFilter({
+ interval,
+ user,
+ repo,
+ branch = 'HEAD',
+ authorFilter,
+ }) {
+ const since =
+ this.constructor.getIntervalQueryStartDate({ interval }) || undefined
+
+ return this._request({
+ url: `/repos/${user}/${repo}/commits`,
+ options: {
+ searchParams: {
+ sha: branch,
+ author: authorFilter,
+ per_page: '1',
+ since,
+ },
+ },
+ httpErrors: httpErrorsFor('repo or branch not found'),
+ })
+ }
+
+ static transform({ data }) {
+ const {
+ repository: { object: repo },
+ } = data
+
+ if (!repo) {
+ throw new InvalidResponse({ prettyMessage: 'invalid branch' })
+ }
+
+ return repo.history.totalCount
+ }
+
+ static transformAuthorFilter({ res }) {
+ const parsed = parseLinkHeader(res.headers.link)
+
+ if (!parsed) {
+ return 0
}
- let wantedWeekData
- switch (interval) {
- case 'y':
- wantedWeekData = weekTotals
- break
- case '4w':
- wantedWeekData = weekTotals.slice(-4)
- break
- case 'w':
- wantedWeekData = weekTotals.slice(-2, -1)
- break
- default:
- throw Error('Unhandled case')
+ return parsed.last.page
+ }
+
+ static getIntervalQueryStartDate({ interval }) {
+ const now = new Date()
+
+ if (interval === 't') {
+ return null
+ } else if (interval === 'y') {
+ now.setUTCFullYear(now.getUTCFullYear() - 1)
+ } else if (interval === 'm' || interval === '4w') {
+ now.setUTCDate(now.getUTCDate() - 30)
+ } else {
+ now.setUTCDate(now.getUTCDate() - 7)
}
- return wantedWeekData.reduce((sum, weekTotal) => sum + weekTotal, 0)
+ return now.toISOString()
}
- async handle({ interval, user, repo }) {
- const weekData = await this._requestJson({
- url: `/repos/${user}/${repo}/stats/commit_activity`,
- schema,
- errorMessages: errorMessagesFor(),
- })
- const commitCount = this.constructor.transform({ interval, weekData })
- return this.constructor.render({ interval, commitCount })
+ async handle({ interval, user, repo, branch }, { authorFilter }) {
+ let commitCount
+ if (authorFilter) {
+ const authorFilterRes = await this.fetchAuthorFilter({
+ interval,
+ user,
+ repo,
+ branch,
+ authorFilter,
+ })
+ commitCount = this.constructor.transformAuthorFilter(authorFilterRes)
+ } else {
+ const json = await this.fetch({ interval, user, repo, branch })
+ commitCount = this.constructor.transform(json)
+ }
+ return this.constructor.render({ interval, commitCount, authorFilter })
}
}
diff --git a/services/github/github-commit-activity.spec.js b/services/github/github-commit-activity.spec.js
new file mode 100644
index 0000000000000..8096b84196437
--- /dev/null
+++ b/services/github/github-commit-activity.spec.js
@@ -0,0 +1,64 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { InvalidResponse } from '../index.js'
+import GitHubCommitActivity from './github-commit-activity.service.js'
+
+describe('GitHubCommitActivity', function () {
+ describe('transform', function () {
+ it('throws InvalidResponse on invalid branch and null object', function () {
+ expect(() =>
+ GitHubCommitActivity.transform({
+ data: { repository: { object: null } },
+ }),
+ )
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'invalid branch')
+ })
+ })
+ describe('getIntervalQueryStartDate', function () {
+ /** @type {sinon.SinonFakeTimers} */
+ let clock
+ beforeEach(function () {
+ clock = sinon.useFakeTimers()
+ })
+ afterEach(function () {
+ clock.restore()
+ })
+
+ it('provides correct value for yearly interval', function () {
+ clock.tick(new Date('2021-08-28T02:21:34.000Z').getTime())
+ expect(
+ GitHubCommitActivity.getIntervalQueryStartDate({
+ interval: 'y',
+ }),
+ ).to.equal('2020-08-28T02:21:34.000Z')
+ })
+
+ it('provides correct value for simple monthly interval', function () {
+ clock.tick(new Date('2021-03-31T02:21:34.000Z').getTime())
+ expect(
+ GitHubCommitActivity.getIntervalQueryStartDate({
+ interval: 'm',
+ }),
+ ).to.equal('2021-03-01T02:21:34.000Z')
+ })
+
+ it('provides correct value for fun monthly interval', function () {
+ clock.tick(new Date('2021-03-07T02:21:34.000Z').getTime())
+ expect(
+ GitHubCommitActivity.getIntervalQueryStartDate({
+ interval: '4w',
+ }),
+ ).to.equal('2021-02-05T02:21:34.000Z')
+ })
+
+ it('provides correct value for weekly interval', function () {
+ clock.tick(new Date('2021-12-31T23:59:34.000Z').getTime())
+ expect(
+ GitHubCommitActivity.getIntervalQueryStartDate({
+ interval: 'w',
+ }),
+ ).to.equal('2021-12-24T23:59:34.000Z')
+ })
+ })
+})
diff --git a/services/github/github-commit-activity.tester.js b/services/github/github-commit-activity.tester.js
index 701d3723e18ce..1efa3bf119c7e 100644
--- a/services/github/github-commit-activity.tester.js
+++ b/services/github/github-commit-activity.tester.js
@@ -1,20 +1,53 @@
-'use strict'
+import Joi from 'joi'
+import {
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+ isMetric,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+const isCommitActivity = Joi.alternatives().try(
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+)
-t.create('commit activity (1 year)')
- .get('/y/eslint/eslint.json')
+const authorFilterUser = 'jnullj'
+
+t.create('commit activity (total)').get('/t/badges/shields.json').expectBadge({
+ label: 'commits',
+ message: isMetric,
+})
+
+t.create('commit activity (total) by author')
+ .get(`/t/badges/shields.json?authorFilter=${authorFilterUser}`)
.expectBadge({
- label: 'commit activity',
- message: isMetricOverTimePeriod,
+ label: `commits by ${authorFilterUser}`,
+ message: isMetric,
})
-t.create('commit activity (1 month)')
- .get('/m/eslint/eslint.json')
+t.create('commit activity (1 year)').get('/y/eslint/eslint.json').expectBadge({
+ label: 'commit activity',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('commit activity (1 year) by author')
+ .get(`/y/badges/shields.json?authorFilter=${authorFilterUser}`)
.expectBadge({
- label: 'commit activity',
- message: isMetricOverTimePeriod,
+ label: `commit activity by ${authorFilterUser}`,
+ message: isCommitActivity,
+ })
+
+t.create('commit activity (1 month)').get('/m/eslint/eslint.json').expectBadge({
+ label: 'commit activity',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('commit activity (1 month) by author')
+ .get(`/m/badges/shields.json?authorFilter=${authorFilterUser}`)
+ .expectBadge({
+ label: `commit activity by ${authorFilterUser}`,
+ message: isCommitActivity,
})
t.create('commit activity (4 weeks)')
@@ -24,11 +57,37 @@ t.create('commit activity (4 weeks)')
message: isMetricOverTimePeriod,
})
-t.create('commit activity (1 week)')
- .get('/w/eslint/eslint.json')
+t.create('commit activity (4 weeks) by author')
+ .get(`/4w/badges/shields.json?authorFilter=${authorFilterUser}`)
+ .expectBadge({
+ label: `commit activity by ${authorFilterUser}`,
+ message: isCommitActivity,
+ })
+
+t.create('commit activity (1 week)').get('/w/eslint/eslint.json').expectBadge({
+ label: 'commit activity',
+ message: isCommitActivity,
+})
+
+t.create('commit activity (1 week) by author')
+ .get(`/w/badges/shields.json?authorFilter=${authorFilterUser}`)
+ .expectBadge({
+ label: `commit activity by ${authorFilterUser}`,
+ message: isCommitActivity,
+ })
+
+t.create('commit activity (custom branch)')
+ .get('/y/badges/squint/main.json')
.expectBadge({
label: 'commit activity',
- message: isMetricOverTimePeriod,
+ message: isCommitActivity,
+ })
+
+t.create('commit activity (custom branch) by author')
+ .get(`/y/badges/squint/main.json?authorFilter=${authorFilterUser}`)
+ .expectBadge({
+ label: `commit activity by ${authorFilterUser}`,
+ message: isCommitActivity,
})
t.create('commit activity (repo not found)')
@@ -37,3 +96,25 @@ t.create('commit activity (repo not found)')
label: 'commit activity',
message: 'repo not found',
})
+
+t.create('commit activity (invalid branch)')
+ .get('/w/badges/shields/invalidBranchName.json')
+ .expectBadge({
+ label: 'commit activity',
+ message: 'invalid branch',
+ })
+
+// test for error handling of author filter as it uses REST and not GraphQL
+t.create('commit activity (repo not found)')
+ .get('/w/badges/helmets.json?authorFilter=zaphod')
+ .expectBadge({
+ label: 'commit activity',
+ message: 'repo or branch not found',
+ })
+
+t.create('commit activity (invalid branch)')
+ .get('/w/badges/shields/invalidBranchName.json?authorFilter=zaphod')
+ .expectBadge({
+ label: 'commit activity',
+ message: 'repo or branch not found',
+ })
diff --git a/services/github/github-commit-status.service.js b/services/github/github-commit-status.service.js
index 4061a451c4b4b..9e7aa0e3c7f51 100644
--- a/services/github/github-commit-status.service.js
+++ b/services/github/github-commit-status.service.js
@@ -1,52 +1,48 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { NotFound, InvalidParameter } = require('..')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { NotFound, InvalidParameter, pathParams } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
// https://stackoverflow.com/a/23969867/893113
status: Joi.equal('identical', 'ahead', 'behind', 'diverged'),
}).required()
-module.exports = class GithubCommitStatus extends GithubAuthV3Service {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'github/commit-status',
- pattern: ':user/:repo/:branch/:commit',
- }
+export default class GithubCommitStatus extends GithubAuthV3Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github/commit-status',
+ pattern: ':user/:repo/:branch/:commit',
}
- static get examples() {
- return [
- {
- title: 'GitHub commit merge status',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- branch: 'master',
- commit: '5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c',
- },
- staticPreview: this.render({
- isInBranch: true,
- branch: 'master',
- }),
- keywords: ['branch'],
- documentation,
+ static openApi = {
+ '/github/commit-status/{user}/{repo}/{branch}/{commit}': {
+ get: {
+ summary: 'GitHub commit merge status',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ {
+ name: 'commit',
+ example: '5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'commit status',
- }
- }
+ static defaultBadgeData = { label: 'commit status' }
static render({ isInBranch, branch }) {
if (isInBranch) {
@@ -68,7 +64,7 @@ module.exports = class GithubCommitStatus extends GithubAuthV3Service {
try {
;({ status } = await this._requestJson({
url: `/repos/${user}/${repo}/compare/${branch}...${commit}`,
- errorMessages: errorMessagesFor('commit or branch not found'),
+ httpErrors: httpErrorsFor('commit or branch not found'),
schema,
}))
} catch (e) {
diff --git a/services/github/github-commit-status.tester.js b/services/github/github-commit-status.tester.js
index baeb41a6150e2..c7718c75382c8 100644
--- a/services/github/github-commit-status.tester.js
+++ b/services/github/github-commit-status.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { invalidJSONString } = require('../response-fixtures')
-const t = (module.exports = require('../tester').createServiceTester())
+import { invalidJSONString } from '../response-fixtures.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('commit status - commit in branch')
.get('/badges/shields/master/5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c.json')
@@ -12,15 +11,15 @@ t.create('commit status - commit in branch')
})
t.create(
- 'commit status - checked commit is identical with the newest commit in branch'
+ 'commit status - checked commit is identical with the newest commit in branch',
)
.get('/badges/shields/master/5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c.json')
.intercept(nock =>
nock('https://api.github.com')
.get(
- '/repos/badges/shields/compare/master...5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c'
+ '/repos/badges/shields/compare/master...5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c',
)
- .reply(200, { status: 'identical' })
+ .reply(200, { status: 'identical' }),
)
.expectBadge({
label: 'commit status',
@@ -46,7 +45,7 @@ t.create('commit status - unknown commit id')
t.create('commit status - unknown branch')
.get(
- '/badges/shields/this-branch-does-not-exist/b551a3a8daf1c48dba32a3eab1edf99b10c28863.json'
+ '/badges/shields/this-branch-does-not-exist/b551a3a8daf1c48dba32a3eab1edf99b10c28863.json',
)
.expectBadge({
label: 'commit status',
@@ -69,9 +68,9 @@ t.create('commit status - 404 with invalid JSON form github')
.intercept(nock =>
nock('https://api.github.com')
.get(
- '/repos/badges/shields/compare/master...5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c'
+ '/repos/badges/shields/compare/master...5d4ab86b1b5ddfb3c4a70a70bd19932c52603b8c',
)
- .reply(404, invalidJSONString)
+ .reply(404, invalidJSONString),
)
.expectBadge({
label: 'commit status',
diff --git a/services/github/github-commits-difference.service.js b/services/github/github-commits-difference.service.js
new file mode 100644
index 0000000000000..14042f035c979
--- /dev/null
+++ b/services/github/github-commits-difference.service.js
@@ -0,0 +1,57 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
+
+const schema = Joi.object({ total_commits: nonNegativeInteger }).required()
+
+const queryParamSchema = Joi.object({
+ base: Joi.string().required(),
+ head: Joi.string().required(),
+}).required()
+
+export default class GithubCommitsDifference extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = {
+ base: 'github/commits-difference',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/commits-difference/{user}/{repo}': {
+ get: {
+ summary: 'GitHub commits difference between two branches/tags/commits',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'microsoft' }),
+ pathParam({ name: 'repo', example: 'vscode' }),
+ queryParam({ name: 'base', example: '1.60.0', required: true }),
+ queryParam({ name: 'head', example: '82f2db7', required: true }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'commits difference' }
+
+ static render({ commitCount }) {
+ return {
+ message: metric(commitCount),
+ color: 'blue',
+ }
+ }
+
+ async handle({ user, repo }, { base, head }) {
+ const notFoundMessage = 'could not establish commit difference between refs'
+ const { total_commits: commitCount } = await this._requestJson({
+ schema,
+ url: `/repos/${user}/${repo}/compare/${base}...${head}`,
+ httpErrors: httpErrorsFor(notFoundMessage),
+ })
+
+ return this.constructor.render({ commitCount })
+ }
+}
diff --git a/services/github/github-commits-difference.tester.js b/services/github/github-commits-difference.tester.js
new file mode 100644
index 0000000000000..0d47bf6195029
--- /dev/null
+++ b/services/github/github-commits-difference.tester.js
@@ -0,0 +1,43 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Commits difference - correct, between branches')
+ .get('/microsoft/vscode.json?base=standalone/0.1.x&head=release/1.21')
+ .expectBadge({
+ label: 'commits difference',
+ message: isMetric,
+ color: 'blue',
+ })
+
+t.create('Commits difference - correct, between tags')
+ .get('/microsoft/vscode.json?base=1.58.0&head=1.59.0')
+ .expectBadge({
+ label: 'commits difference',
+ message: isMetric,
+ color: 'blue',
+ })
+
+t.create('Commits difference - correct, between commits')
+ .get('/microsoft/vscode.json?base=3d82ef7&head=82f2db7')
+ .expectBadge({
+ label: 'commits difference',
+ message: isMetric,
+ color: 'blue',
+ })
+
+t.create('Commits difference - incorrect, between commits')
+ .get('/microsoft/vscode.json?base=fffffff&head=82f2db7')
+ .expectBadge({
+ label: 'commits difference',
+ message: 'could not establish commit difference between refs',
+ color: 'red',
+ })
+
+t.create('Commits difference - incorrect, missing head')
+ .get('/microsoft/vscode.json?base=fffffff')
+ .expectBadge({
+ label: 'commits difference',
+ message: 'invalid query parameter: head',
+ color: 'red',
+ })
diff --git a/services/github/github-commits-since.service.js b/services/github/github-commits-since.service.js
index dd19d395e85af..f7835ad22f475 100644
--- a/services/github/github-commits-since.service.js
+++ b/services/github/github-commits-since.service.js
@@ -1,143 +1,85 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const {
+import Joi from 'joi'
+import { pathParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
fetchLatestRelease,
queryParamSchema,
-} = require('./github-common-release')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+ openApiQueryParams,
+} from './github-common-release.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({ ahead_by: nonNegativeInteger }).required()
-module.exports = class GithubCommitsSince extends GithubAuthV3Service {
- static get category() {
- return 'activity'
- }
+const latestDocs = `
+The include_prereleases, sort and filter params can be used to configure how we determine the latest version.
+`
- static get route() {
- return {
- base: 'github/commits-since',
- pattern: ':user/:repo/:version/:branch*',
- queryParamSchema,
- }
+export default class GithubCommitsSince extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = {
+ base: 'github/commits-since',
+ pattern: ':user/:repo/:version/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub commits since tagged version',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: '3.4.7',
- },
- staticPreview: this.render({
- version: '3.4.7',
- commitCount: 4225,
- }),
- documentation,
+ static openApi = {
+ '/github/commits-since/{user}/{repo}/{version}': {
+ get: {
+ summary: 'GitHub commits since tagged version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'SubtitleEdit' }),
+ pathParam({ name: 'repo', example: 'subtitleedit' }),
+ pathParam({
+ name: 'version',
+ example: '3.4.7',
+ }),
+ ],
},
- {
- title: 'GitHub commits since tagged version (branch)',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: '3.4.7',
- branch: 'master',
- },
- staticPreview: this.render({
- version: '3.4.7',
- commitCount: 4225,
- }),
- documentation,
+ },
+ '/github/commits-since/{user}/{repo}/{version}/{branch}': {
+ get: {
+ summary: 'GitHub commits since tagged version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'SubtitleEdit' }),
+ pathParam({ name: 'repo', example: 'subtitleedit' }),
+ pathParam({
+ name: 'version',
+ example: '3.4.7',
+ }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ],
},
- {
- title: 'GitHub commits since latest release (by date)',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: 'latest',
- },
- staticPreview: this.render({
- version: '3.5.7',
- commitCount: 157,
- }),
- documentation,
+ },
+ '/github/commits-since/{user}/{repo}/latest': {
+ get: {
+ summary: 'GitHub commits since latest release',
+ description: documentation + latestDocs,
+ parameters: [
+ pathParam({ name: 'user', example: 'SubtitleEdit' }),
+ pathParam({ name: 'repo', example: 'subtitleedit' }),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'GitHub commits since latest release (by date) for a branch',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: 'latest',
- branch: 'master',
- },
- staticPreview: this.render({
- version: '3.5.7',
- commitCount: 157,
- }),
- documentation,
+ },
+ '/github/commits-since/{user}/{repo}/latest/{branch}': {
+ get: {
+ summary: 'GitHub commits since latest release (branch)',
+ description: documentation + latestDocs,
+ parameters: [
+ pathParam({ name: 'user', example: 'SubtitleEdit' }),
+ pathParam({ name: 'repo', example: 'subtitleedit' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
},
- {
- title:
- 'GitHub commits since latest release (by date including pre-releases)',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: 'latest',
- },
- queryParams: { include_prereleases: null },
- staticPreview: this.render({
- version: 'v3.5.8-alpha.1',
- isPrerelease: true,
- commitCount: 158,
- }),
- documentation,
- },
- {
- title: 'GitHub commits since latest release (by SemVer)',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: 'latest',
- },
- queryParams: { sort: 'semver' },
- staticPreview: this.render({
- version: 'v4.0.1',
- sort: 'semver',
- commitCount: 200,
- }),
- documentation,
- },
- {
- title:
- 'GitHub commits since latest release (by SemVer including pre-releases)',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- version: 'latest',
- },
- queryParams: { sort: 'semver', include_prereleases: null },
- staticPreview: this.render({
- version: 'v4.0.2-alpha.1',
- sort: 'semver',
- isPrerelease: true,
- commitCount: 201,
- }),
- documentation,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'github',
- namedLogo: 'github',
- }
- }
+ static defaultBadgeData = { label: 'github' }
static render({ version, commitCount }) {
return {
@@ -155,7 +97,7 @@ module.exports = class GithubCommitsSince extends GithubAuthV3Service {
user,
repo,
},
- queryParams
+ queryParams,
))
}
@@ -164,8 +106,8 @@ module.exports = class GithubCommitsSince extends GithubAuthV3Service {
: 'repo or version not found'
const { ahead_by: commitCount } = await this._requestJson({
schema,
- url: `/repos/${user}/${repo}/compare/${version}...${branch || 'master'}`,
- errorMessages: errorMessagesFor(notFoundMessage),
+ url: `/repos/${user}/${repo}/compare/${version}...${branch || 'HEAD'}`,
+ httpErrors: httpErrorsFor(notFoundMessage),
})
return this.constructor.render({ version, commitCount })
diff --git a/services/github/github-commits-since.tester.js b/services/github/github-commits-since.tester.js
index 049e9b01b1dde..35d325cdeb244 100644
--- a/services/github/github-commits-since.tester.js
+++ b/services/github/github-commits-since.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex, isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex, isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isCommitsSince = withRegex(/^(commits since){1}[\s\S]+$/)
@@ -14,7 +13,9 @@ t.create('Commits since')
})
t.create('Commits since (branch)')
- .get('/badges/shields/60be4859585650e8c2b87669e3a39d98ca084e98/gh-pages.json')
+ .get(
+ '/badges/shields/8b87fac3a1538ec20ff20983faf4b6f7e722ef87/historical.json',
+ )
.expectBadge({
label: isCommitsSince,
message: isMetric,
@@ -28,7 +29,7 @@ t.create('Commits since by latest release')
})
t.create('Commits since by latest release (branch)')
- .get('/microsoft/typescript/latest/master.json')
+ .get('/microsoft/typescript/latest/main.json')
.expectBadge({
label: isCommitsSince,
message: isMetric,
@@ -42,7 +43,7 @@ t.create('Commits since by latest SemVer release')
})
t.create('Commits since by latest pre-release')
- .get('/microsoft/typescript/latest/master.json?include_prereleases')
+ .get('/microsoft/typescript/latest/main.json?include_prereleases')
.expectBadge({
label: isCommitsSince,
message: isMetric,
@@ -57,7 +58,7 @@ t.create('Commits since (version not found)')
t.create('Commits since (branch not found)')
.get(
- '/badges/shields/a0663d8da53fb712472c02665e6ff7547ba945b7/not-a-branch.json'
+ '/badges/shields/a0663d8da53fb712472c02665e6ff7547ba945b7/not-a-branch.json',
)
.expectBadge({
label: 'github',
diff --git a/services/github/github-common-fetch.js b/services/github/github-common-fetch.js
index 7486a8bcfffd4..9c1611a7eefa5 100644
--- a/services/github/github-common-fetch.js
+++ b/services/github/github-common-fetch.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { InvalidResponse } = require('..')
-const { errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { InvalidResponse } from '../index.js'
+import { httpErrorsFor } from './github-helpers.js'
const issueSchema = Joi.object({
head: Joi.object({
@@ -14,7 +12,7 @@ async function fetchIssue(serviceInstance, { user, repo, number }) {
return serviceInstance._requestJson({
schema: issueSchema,
url: `/repos/${user}/${repo}/pulls/${number}`,
- errorMessages: errorMessagesFor('pull request or repo not found'),
+ httpErrors: httpErrorsFor('pull request or repo not found'),
})
}
@@ -26,17 +24,17 @@ const contentSchema = Joi.object({
async function fetchRepoContent(
serviceInstance,
- { user, repo, branch = 'master', filename }
+ { user, repo, branch = 'HEAD', filename },
) {
- const errorMessages = errorMessagesFor(
- `repo not found, branch not found, or ${filename} missing`
+ const httpErrors = httpErrorsFor(
+ `repo not found, branch not found, or ${filename} missing`,
)
if (serviceInstance.staticAuthConfigured) {
const { content } = await serviceInstance._requestJson({
schema: contentSchema,
url: `/repos/${user}/${repo}/contents/${filename}`,
- options: { qs: { ref: branch } },
- errorMessages,
+ options: { searchParams: { ref: branch } },
+ httpErrors,
})
try {
@@ -47,7 +45,7 @@ async function fetchRepoContent(
} else {
const { buffer } = await serviceInstance._request({
url: `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${filename}`,
- errorMessages,
+ httpErrors,
})
return buffer
}
@@ -55,7 +53,7 @@ async function fetchRepoContent(
async function fetchJsonFromRepo(
serviceInstance,
- { schema, user, repo, branch = 'master', filename }
+ { schema, user, repo, branch = 'HEAD', filename },
) {
if (serviceInstance.staticAuthConfigured) {
const buffer = await fetchRepoContent(serviceInstance, {
@@ -70,15 +68,11 @@ async function fetchJsonFromRepo(
return serviceInstance._requestJson({
schema,
url: `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${filename}`,
- errorMessages: errorMessagesFor(
- `repo not found, branch not found, or ${filename} missing`
+ httpErrors: httpErrorsFor(
+ `repo not found, branch not found, or ${filename} missing`,
),
})
}
}
-module.exports = {
- fetchIssue,
- fetchRepoContent,
- fetchJsonFromRepo,
-}
+export { fetchIssue, fetchRepoContent, fetchJsonFromRepo }
diff --git a/services/github/github-common-release.js b/services/github/github-common-release.js
index 63a91265d0aed..d7e7b1444096e 100644
--- a/services/github/github-common-release.js
+++ b/services/github/github-common-release.js
@@ -1,19 +1,26 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { latest } = require('../version')
-const { NotFound } = require('..')
-const { errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { matcher } from 'matcher'
+import { nonNegativeInteger } from '../validators.js'
+import { latest } from '../version.js'
+import { NotFound, queryParams } from '../index.js'
+import { httpErrorsFor } from './github-helpers.js'
const releaseInfoSchema = Joi.object({
+ assets: Joi.array()
+ .items({
+ name: Joi.string().required(),
+ download_count: nonNegativeInteger,
+ })
+ .required(),
tag_name: Joi.string().required(),
+ name: Joi.string().allow(null).allow(''),
prerelease: Joi.boolean().required(),
}).required()
// Fetch the 'latest' release as defined by the GitHub API
async function fetchLatestGitHubRelease(serviceInstance, { user, repo }) {
const commonAttrs = {
- errorMessages: errorMessagesFor('no releases or repo not found'),
+ httpErrors: httpErrorsFor('no releases or repo not found'),
}
const releaseInfo = await serviceInstance._requestJson({
schema: releaseInfoSchema,
@@ -25,33 +32,29 @@ async function fetchLatestGitHubRelease(serviceInstance, { user, repo }) {
const releaseInfoArraySchema = Joi.alternatives().try(
Joi.array().items(releaseInfoSchema),
- Joi.array().length(0)
+ Joi.array().length(0),
)
async function fetchReleases(serviceInstance, { user, repo }) {
const commonAttrs = {
- errorMessages: errorMessagesFor('repo not found'),
+ httpErrors: httpErrorsFor('repo not found'),
}
const releases = await serviceInstance._requestJson({
url: `/repos/${user}/${repo}/releases`,
schema: releaseInfoArraySchema,
...commonAttrs,
+ options: { searchParams: { per_page: 100 } },
})
return releases
}
function getLatestRelease({ releases, sort, includePrereleases }) {
if (sort === 'semver') {
- const latestRelease = latest(
+ const latestTagName = latest(
releases.map(release => release.tag_name),
- {
- pre: includePrereleases,
- }
- )
- const kvpairs = Object.assign(
- ...releases.map(release => ({ [release.tag_name]: release.prerelease }))
+ { pre: includePrereleases },
)
- return { tag_name: latestRelease, prerelease: kvpairs[latestRelease] }
+ return releases.find(({ tag_name: tagName }) => tagName === latestTagName)
}
if (!includePrereleases) {
@@ -64,23 +67,70 @@ function getLatestRelease({ releases, sort, includePrereleases }) {
return releases[0]
}
+const sortEnum = ['date', 'semver']
+
const queryParamSchema = Joi.object({
include_prereleases: Joi.equal(''),
sort: Joi.string()
- .valid('date', 'semver')
+ .valid(...sortEnum)
.default('date'),
+ filter: Joi.string(),
}).required()
+const filterDocs = `
+The filter param can be used to apply a filter to the
+project's tag or release names before selecting the latest from the list.
+Two constructs are available: * is a wildcard matching zero
+or more characters, and if the pattern starts with a !,
+the whole pattern is negated.
+`
+
+const openApiQueryParams = queryParams(
+ {
+ name: 'include_prereleases',
+ example: null,
+ schema: { type: 'boolean' },
+ },
+ {
+ name: 'sort',
+ example: 'semver',
+ schema: { type: 'string', enum: sortEnum },
+ },
+ { name: 'filter', example: '*beta*', description: filterDocs },
+)
+
+function applyFilter({ releases, filter, displayName }) {
+ if (!filter) {
+ return releases
+ }
+ if (displayName === 'tag') {
+ const filteredTagNames = matcher(
+ releases.map(release => release.tag_name),
+ filter,
+ )
+ return releases.filter(release =>
+ filteredTagNames.includes(release.tag_name),
+ )
+ }
+ const filteredReleaseNames = matcher(
+ releases.map(release => release.name),
+ filter,
+ )
+ return releases.filter(release => filteredReleaseNames.includes(release.name))
+}
+
// Fetch the latest release as defined by query params
async function fetchLatestRelease(
serviceInstance,
{ user, repo },
- queryParams
+ queryParams,
) {
const sort = queryParams.sort
const includePrereleases = queryParams.include_prereleases !== undefined
+ const filter = queryParams.filter
+ const displayName = queryParams.display_name
- if (!includePrereleases && sort === 'date') {
+ if (!includePrereleases && sort === 'date' && !filter) {
const releaseInfo = await fetchLatestGitHubRelease(serviceInstance, {
user,
repo,
@@ -88,16 +138,23 @@ async function fetchLatestRelease(
return releaseInfo
}
- const releases = await fetchReleases(serviceInstance, { user, repo })
+ const releases = applyFilter({
+ releases: await fetchReleases(serviceInstance, { user, repo }),
+ filter,
+ displayName,
+ })
if (releases.length === 0) {
- throw new NotFound({ prettyMessage: 'no releases' })
+ const prettyMessage = filter
+ ? 'no matching releases found'
+ : 'no releases found'
+ throw new NotFound({ prettyMessage })
}
const latestRelease = getLatestRelease({ releases, sort, includePrereleases })
return latestRelease
}
-module.exports = {
- fetchLatestRelease,
- queryParamSchema,
- _getLatestRelease: getLatestRelease, // currently only used for tests
-}
+export { fetchLatestRelease, queryParamSchema, openApiQueryParams }
+
+// currently only used for tests
+export const _getLatestRelease = getLatestRelease
+export const _applyFilter = applyFilter
diff --git a/services/github/github-common-release.spec.js b/services/github/github-common-release.spec.js
index 28e40d731b332..536eb38321ebd 100644
--- a/services/github/github-common-release.spec.js
+++ b/services/github/github-common-release.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { _applyFilter, _getLatestRelease } from './github-common-release.js'
-const { test, given } = require('sazerac')
-const { _getLatestRelease } = require('./github-common-release')
-
-describe('GithubRelease', function() {
+describe('GithubRelease', function () {
test(_getLatestRelease, () => {
const releaseFixture = [
{ tag_name: 'cheese', prerelease: false }, // any old string
@@ -44,4 +42,50 @@ describe('GithubRelease', function() {
includePrereleases: false,
}).expect({ tag_name: '1.2.0-beta', prerelease: true })
})
+
+ test(_applyFilter, () => {
+ const releases = [
+ { name: 'release/1.1.0', tag_name: 'tag/1.1.0', prerelease: false },
+ { name: 'release/1.2.0', tag_name: 'tag/1.2.0', prerelease: false },
+ {
+ name: 'release/server-2022-01-01',
+ tag_name: 'tag/server-2022-01-01',
+ prerelease: false,
+ },
+ ]
+
+ given({ releases, filter: undefined }).expect(releases)
+ given({ releases, filter: '' }).expect(releases)
+ given({ releases, filter: '*' }).expect(releases)
+ given({ releases, filter: '!*' }).expect([])
+ given({ releases, filter: 'foo' }).expect([])
+ given({ releases, filter: 'release/server-*' }).expect([
+ {
+ name: 'release/server-2022-01-01',
+ tag_name: 'tag/server-2022-01-01',
+ prerelease: false,
+ },
+ ])
+ given({ releases, filter: '!release/server-*' }).expect([
+ { name: 'release/1.1.0', tag_name: 'tag/1.1.0', prerelease: false },
+ { name: 'release/1.2.0', tag_name: 'tag/1.2.0', prerelease: false },
+ ])
+
+ given({ releases, displayName: 'tag', filter: undefined }).expect(releases)
+ given({ releases, displayName: 'tag', filter: '' }).expect(releases)
+ given({ releases, displayName: 'tag', filter: '*' }).expect(releases)
+ given({ releases, displayName: 'tag', filter: '!*' }).expect([])
+ given({ releases, displayName: 'tag', filter: 'foo' }).expect([])
+ given({ releases, displayName: 'tag', filter: 'tag/server-*' }).expect([
+ {
+ name: 'release/server-2022-01-01',
+ tag_name: 'tag/server-2022-01-01',
+ prerelease: false,
+ },
+ ])
+ given({ releases, displayName: 'tag', filter: '!tag/server-*' }).expect([
+ { name: 'release/1.1.0', tag_name: 'tag/1.1.0', prerelease: false },
+ { name: 'release/1.2.0', tag_name: 'tag/1.2.0', prerelease: false },
+ ])
+ })
})
diff --git a/services/github/github-constellation.js b/services/github/github-constellation.js
index a9754bc6bf426..ed85311f2082d 100644
--- a/services/github/github-constellation.js
+++ b/services/github/github-constellation.js
@@ -1,14 +1,8 @@
-'use strict'
-
-const path = require('path')
-const { AuthHelper } = require('../../core/base-service/auth-helper')
-const RedisTokenPersistence = require('../../core/token-pooling/redis-token-persistence')
-const FsTokenPersistence = require('../../core/token-pooling/fs-token-persistence')
-const serverSecrets = require('../../lib/server-secrets')
-const log = require('../../core/server/log')
-const GithubApiProvider = require('./github-api-provider')
-const { setRoutes: setAdminRoutes } = require('./auth/admin')
-const { setRoutes: setAcceptorRoutes } = require('./auth/acceptor')
+import { AuthHelper } from '../../core/base-service/auth-helper.js'
+import SqlTokenPersistence from '../../core/token-pooling/sql-token-persistence.js'
+import log from '../../core/server/log.js'
+import GithubApiProvider from './github-api-provider.js'
+import { setRoutes as setAcceptorRoutes } from './auth/acceptor.js'
// Convenience class with all the stuff related to the Github API and its
// authorization tokens, to simplify server initialization.
@@ -21,38 +15,39 @@ class GithubConstellation {
authorizedOrigins: ['https://api.github.com'],
isRequired: true,
},
- config
+ config,
)
}
constructor(config) {
this._debugEnabled = config.service.debug.enabled
this._debugIntervalSeconds = config.service.debug.intervalSeconds
+ this._metricsIntervalSeconds = config.metricsIntervalSeconds
- const { redis_url: redisUrl } = config.private
- const { dir: persistenceDir } = config.persistence
- if (redisUrl) {
- log('RedisTokenPersistence configured with redisUrl')
- this.persistence = new RedisTokenPersistence({
- url: redisUrl,
- key: 'githubUserTokens',
+ let authType = GithubApiProvider.AUTH_TYPES.NO_AUTH
+
+ const { postgres_url: pgUrl, gh_token: globalToken } = config.private
+ if (pgUrl) {
+ log.log('Github Token persistence configured with pgUrl')
+ this.persistence = new SqlTokenPersistence({
+ url: pgUrl,
+ table: 'github_user_tokens',
})
- } else {
- const userTokensPath = path.resolve(
- persistenceDir,
- 'github-user-tokens.json'
- )
- log(`FsTokenPersistence configured with ${userTokensPath}`)
- this.persistence = new FsTokenPersistence({ path: userTokensPath })
+ authType = GithubApiProvider.AUTH_TYPES.TOKEN_POOL
+ }
+
+ if (globalToken) {
+ authType = GithubApiProvider.AUTH_TYPES.GLOBAL_TOKEN
}
- const globalToken = serverSecrets.gh_token
- const baseUrl = process.env.GITHUB_URL || 'https://api.github.com'
+ log.log(`Github using auth type: ${authType}`)
+
this.apiProvider = new GithubApiProvider({
- baseUrl,
+ baseUrl: config.service.baseUri,
globalToken,
- withPooling: !globalToken,
+ authType,
onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString),
+ restApiVersion: config.service.restApiVersion,
})
this.oauthHelper = this.constructor._createOauthHelper(config)
@@ -61,17 +56,35 @@ class GithubConstellation {
scheduleDebugLogging() {
if (this._debugEnabled) {
this.debugInterval = setInterval(() => {
- log(this.apiProvider.getTokenDebugInfo())
+ const debugInfo = this.apiProvider.getTokenDebugInfo()
+ log.log(debugInfo)
}, 1000 * this._debugIntervalSeconds)
}
}
- async initialize(server) {
- if (!this.apiProvider.withPooling) {
+ scheduleMetricsCollection() {
+ if (this.metricInstance) {
+ this.metricsInterval = setInterval(() => {
+ const debugInfo = this.apiProvider.getTokenDebugInfo()
+ this.metricInstance.noteGithubTokenPoolMetrics(debugInfo)
+ }, 1000 * this._metricsIntervalSeconds)
+ }
+ }
+
+ async initialize(server, metricInstance) {
+ if (this.apiProvider.authType !== GithubApiProvider.AUTH_TYPES.TOKEN_POOL) {
return
}
+ this.metricInstance = metricInstance
+ this.apiProvider.metricInstance = metricInstance
+
this.scheduleDebugLogging()
+ this.scheduleMetricsCollection()
+
+ if (!this.persistence) {
+ return
+ }
let tokens = []
try {
@@ -80,26 +93,28 @@ class GithubConstellation {
log.error(e)
}
- tokens.forEach(tokenString => {
- this.apiProvider.addToken(tokenString)
+ tokens.forEach(({ token, scopes }) => {
+ this.apiProvider.addToken(token, { scopes })
})
- setAdminRoutes(this.apiProvider, server)
-
if (this.oauthHelper.isConfigured) {
setAcceptorRoutes({
server,
authHelper: this.oauthHelper,
- onTokenAccepted: tokenString => this.onTokenAdded(tokenString),
+ onTokenAccepted: (tokenString, data) =>
+ this.onTokenAdded(tokenString, data),
})
}
}
- onTokenAdded(tokenString) {
- this.apiProvider.addToken(tokenString)
+ onTokenAdded(tokenString, data) {
+ if (!this.persistence) {
+ throw Error('Token persistence is not configured')
+ }
+ this.apiProvider.addToken(tokenString, data)
process.nextTick(async () => {
try {
- await this.persistence.noteTokenAdded(tokenString)
+ await this.persistence.noteTokenAdded(tokenString, data)
} catch (e) {
log.error(e)
}
@@ -107,13 +122,15 @@ class GithubConstellation {
}
onTokenInvalidated(tokenString) {
- process.nextTick(async () => {
- try {
- await this.persistence.noteTokenRemoved(tokenString)
- } catch (e) {
- log.error(e)
- }
- })
+ if (this.persistence) {
+ process.nextTick(async () => {
+ try {
+ await this.persistence.noteTokenRemoved(tokenString)
+ } catch (e) {
+ log.error(e)
+ }
+ })
+ }
}
async stop() {
@@ -122,12 +139,19 @@ class GithubConstellation {
this.debugInterval = undefined
}
- try {
- await this.persistence.stop()
- } catch (e) {
- log.error(e)
+ if (this.metricsInterval) {
+ clearInterval(this.metricsInterval)
+ this.metricsInterval = undefined
+ }
+
+ if (this.persistence) {
+ try {
+ await this.persistence.stop()
+ } catch (e) {
+ log.error(e)
+ }
}
}
}
-module.exports = GithubConstellation
+export default GithubConstellation
diff --git a/services/github/github-contributors.service.js b/services/github/github-contributors.service.js
index 502b1d22268e6..991232d48653a 100644
--- a/services/github/github-contributors.service.js
+++ b/services/github/github-contributors.service.js
@@ -1,56 +1,66 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const parseLinkHeader = require('parse-link-header')
-const { renderContributorBadge } = require('../contributor-count')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import parseLinkHeader from 'parse-link-header'
+import { pathParams } from '../index.js'
+import { renderContributorBadge } from '../contributor-count.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
// All we do is check its length.
const schema = Joi.array().items(Joi.object())
-module.exports = class GithubContributors extends GithubAuthV3Service {
- static get category() {
- return 'activity'
- }
+const documentationWithNote = [
+ documentation,
+ 'Note: Co-authors are not included in the count due to endpoint limitations.',
+].join('\n\n')
- static get route() {
- return {
- base: 'github',
- pattern: ':variant(contributors|contributors-anon)/:user/:repo',
- }
+export default class GithubContributors extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = {
+ base: 'github',
+ // note we call this param 'metric' instead of 'variant' because of
+ // https://github.com/badges/shields/issues/10323
+ pattern: ':metric(contributors|contributors-anon)/:user/:repo',
}
- static get examples() {
- return [
- {
- title: 'GitHub contributors',
- namedParams: {
- variant: 'contributors',
- user: 'cdnjs',
- repo: 'cdnjs',
- },
- staticPreview: this.render({ contributorCount: 397 }),
- documentation,
+ static openApi = {
+ '/github/{metric}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub contributors',
+ description: documentationWithNote,
+ parameters: pathParams(
+ {
+ name: 'metric',
+ example: 'contributors',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ description:
+ '`contributors-anon` includes anonymous commits, whereas `contributors` excludes them.',
+ },
+ {
+ name: 'user',
+ example: 'cdnjs',
+ },
+ {
+ name: 'repo',
+ example: 'cdnjs',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'contributors' }
- }
+ static defaultBadgeData = { label: 'contributors' }
static render({ contributorCount }) {
return renderContributorBadge({ contributorCount })
}
- async handle({ variant, user, repo }) {
- const isAnon = variant === 'contributors-anon'
+ async handle({ metric, user, repo }) {
+ const isAnon = metric === 'contributors-anon'
const { res, buffer } = await this._request({
url: `/repos/${user}/${repo}/contributors`,
- options: { qs: { page: '1', per_page: '1', anon: isAnon } },
- errorMessages: errorMessagesFor('repo not found'),
+ options: { searchParams: { page: '1', per_page: '1', anon: isAnon } },
+ httpErrors: httpErrorsFor('repo not found'),
})
const parsed = parseLinkHeader(res.headers.link)
diff --git a/services/github/github-contributors.tester.js b/services/github/github-contributors.tester.js
index bc5cc3ace9887..4495d3b473e5b 100644
--- a/services/github/github-contributors.tester.js
+++ b/services/github/github-contributors.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
-
-t.create('Contributors')
- .get('/contributors/badges/shields.json')
- .expectBadge({
- label: 'contributors',
- message: isMetric,
- })
+t.create('Contributors').get('/contributors/badges/shields.json').expectBadge({
+ label: 'contributors',
+ message: isMetric,
+})
t.create('1 contributor')
.get('/contributors/badges/shields-tests.json')
diff --git a/services/github/github-created-at.service.js b/services/github/github-created-at.service.js
new file mode 100644
index 0000000000000..0e4c654dbb2ce
--- /dev/null
+++ b/services/github/github-created-at.service.js
@@ -0,0 +1,44 @@
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { pathParams } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
+
+const schema = Joi.object({
+ created_at: Joi.date().required(),
+}).required()
+
+export default class GithubCreatedAt extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = { base: 'github/created-at', pattern: ':user/:repo' }
+ static openApi = {
+ '/github/created-at/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Created At',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'mashape',
+ },
+ {
+ name: 'repo',
+ example: 'apistatus',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'created at' }
+
+ async handle({ user, repo }) {
+ const { created_at: createdAt } = await this._requestJson({
+ schema,
+ url: `/repos/${user}/${repo}`,
+ httpErrors: httpErrorsFor('repo not found'),
+ })
+
+ return renderDateBadge(createdAt, true)
+ }
+}
diff --git a/services/github/github-created-at.tester.js b/services/github/github-created-at.tester.js
new file mode 100644
index 0000000000000..0f473468202fc
--- /dev/null
+++ b/services/github/github-created-at.tester.js
@@ -0,0 +1,14 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('created at').get('/erayerdin/firereact.json').expectBadge({
+ label: 'created at',
+ message: isFormattedDate,
+})
+
+t.create('created at').get('/erayerdin/not-a-valid-repo.json').expectBadge({
+ label: 'created at',
+ message: 'repo not found',
+})
diff --git a/services/github/github-deployments.service.js b/services/github/github-deployments.service.js
index 3ebc193e90d00..57c0dffca9cc3 100644
--- a/services/github/github-deployments.service.js
+++ b/services/github/github-deployments.service.js
@@ -1,15 +1,18 @@
-'use strict'
-
-const gql = require('graphql-tag')
-const Joi = require('@hapi/joi')
-const { NotFound } = require('..')
-const { GithubAuthV4Service } = require('./github-auth-service')
-const { documentation, transformErrors } = require('./github-helpers')
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { NotFound, pathParams } from '../index.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
const greenStates = ['SUCCESS']
const redStates = ['ERROR', 'FAILURE']
const blueStates = ['INACTIVE']
-const otherStates = ['IN_PROGRESS', 'QUEUED', 'PENDING']
+const otherStates = ['IN_PROGRESS', 'QUEUED', 'PENDING', 'NO_STATUS', 'WAITING']
+
+const stateToMessageMappings = {
+ IN_PROGRESS: 'in progress',
+ NO_STATUS: 'no status yet',
+}
const allState = greenStates
.concat(redStates)
@@ -31,7 +34,7 @@ const schema = Joi.object({
}),
null,
]),
- })
+ }),
)
.required(),
}).required(),
@@ -39,38 +42,37 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class GithubDeployments extends GithubAuthV4Service {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'github/deployments',
- pattern: ':user/:repo/:environment',
- }
+export default class GithubDeployments extends GithubAuthV4Service {
+ static category = 'other'
+ static route = {
+ base: 'github/deployments',
+ pattern: ':user/:repo/:environment',
}
- static get examples() {
- return [
- {
- title: 'GitHub deployments',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- environment: 'shields-staging',
- },
- staticPreview: this.render({
- state: 'success',
- }),
- documentation,
+ static openApi = {
+ '/github/deployments/{user}/{repo}/{environment}': {
+ get: {
+ summary: 'GitHub deployments',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'environment',
+ example: 'shields-staging',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'state' }
- }
+ static defaultBadgeData = { label: 'state' }
static render({ state }) {
let color
@@ -82,10 +84,8 @@ module.exports = class GithubDeployments extends GithubAuthV4Service {
color = 'blue'
}
- let message
- if (state === 'IN_PROGRESS') {
- message = 'in progress'
- } else {
+ let message = stateToMessageMappings[state]
+ if (!message) {
message = state.toLowerCase()
}
@@ -98,7 +98,7 @@ module.exports = class GithubDeployments extends GithubAuthV4Service {
async fetch({ user, repo, environment }) {
return this._requestGraphql({
query: gql`
- query($user: String!, $repo: String!, $environment: String!) {
+ query ($user: String!, $repo: String!, $environment: String!) {
repository(owner: $user, name: $repo) {
deployments(last: 1, environments: [$environment]) {
nodes {
@@ -123,14 +123,14 @@ module.exports = class GithubDeployments extends GithubAuthV4Service {
// This happens for the brief moment a deployment is created, but no
// status is created for the deployment (yet).
if (data.repository.deployments.nodes[0].latestStatus == null) {
- throw new NotFound({ prettyMessage: 'deployment has no status (yet)' })
+ return { state: 'NO_STATUS' }
}
const state = data.repository.deployments.nodes[0].latestStatus.state
return { state }
}
- async handle({ user, repo, environment }, queryParams) {
+ async handle({ user, repo, environment }) {
const json = await this.fetch({ user, repo, environment })
const { state } = this.transform({ data: json.data })
return this.constructor.render({ state })
diff --git a/services/github/github-deployments.spec.js b/services/github/github-deployments.spec.js
index f9f492f358ba4..bddf923af82a4 100644
--- a/services/github/github-deployments.spec.js
+++ b/services/github/github-deployments.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import GithubDeployments from './github-deployments.service.js'
-const { test, given } = require('sazerac')
-const GithubDeployments = require('./github-deployments.service')
-
-describe('GithubDeployments', function() {
+describe('GithubDeployments', function () {
test(GithubDeployments.render, () => {
given({
state: 'SUCCESS',
@@ -23,6 +21,18 @@ describe('GithubDeployments', function() {
message: 'in progress',
color: undefined,
})
+ given({
+ state: 'WAITING',
+ }).expect({
+ message: 'waiting',
+ color: undefined,
+ })
+ given({
+ state: 'NO_STATUS',
+ }).expect({
+ message: 'no status yet',
+ color: undefined,
+ })
})
test(GithubDeployments.prototype.transform, () => {
@@ -47,7 +57,9 @@ describe('GithubDeployments', function() {
},
},
},
- }).expectError('Not Found: deployment has no status (yet)')
+ }).expect({
+ state: 'NO_STATUS',
+ })
given({
data: {
repository: {
diff --git a/services/github/github-deployments.tester.js b/services/github/github-deployments.tester.js
index 309f3acde7d02..18fca305c9db8 100644
--- a/services/github/github-deployments.tester.js
+++ b/services/github/github-deployments.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const validMessages = [
'success',
@@ -11,6 +10,7 @@ const validMessages = [
'in progress',
'queued',
'pending',
+ 'no status yet',
]
const isValidMessages = Joi.equal(...validMessages).required()
@@ -27,3 +27,19 @@ t.create('Deployments (environment not found)')
label: 'state',
message: 'environment not found',
})
+
+t.create('Deployments (status not yet available)')
+ .get('/badges/shields/shields-staging.json')
+ .intercept(nock =>
+ nock('https://api.github.com/')
+ .post('/graphql')
+ .reply(200, {
+ data: {
+ repository: { deployments: { nodes: [{ latestStatus: null }] } },
+ },
+ }),
+ )
+ .expectBadge({
+ label: 'state',
+ message: 'no status yet',
+ })
diff --git a/services/github/github-directory-file-count.service.js b/services/github/github-directory-file-count.service.js
new file mode 100644
index 0000000000000..48ec5b3f65ed3
--- /dev/null
+++ b/services/github/github-directory-file-count.service.js
@@ -0,0 +1,159 @@
+import Joi from 'joi'
+import gql from 'graphql-tag'
+import { metric } from '../text-formatters.js'
+import { InvalidParameter, pathParam, queryParam } from '../index.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ object: Joi.object({
+ entries: Joi.array().items(
+ Joi.object({
+ type: Joi.string().required(),
+ extension: Joi.string().allow('').required(),
+ }),
+ ),
+ })
+ .allow(null)
+ .required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const typeEnum = ['dir', 'file']
+
+const queryParamSchema = Joi.object({
+ type: Joi.any().valid(...typeEnum),
+ extension: Joi.string(),
+})
+
+const typeDocs =
+ 'Entity to count: directories or files. If not specified, both files and directories are counted. GitHub API has an upper limit of 1,000 files for a directory. If a directory contains files above the limit, the badge will show an inaccurate count.'
+const extensionDocs =
+ 'Filter to files of type. Specify the extension without a leading dot. For instance for `.js` extension pass `js`. This param is only applicable if type is `file`'
+
+export default class GithubDirectoryFileCount extends GithubAuthV4Service {
+ static category = 'size'
+
+ static route = {
+ base: 'github/directory-file-count',
+ pattern: ':user/:repo/:path*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/directory-file-count/{user}/{repo}': {
+ get: {
+ summary: 'GitHub repo file or directory count',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ queryParam({
+ name: 'type',
+ example: 'file',
+ schema: { type: 'string', enum: typeEnum },
+ description: typeDocs,
+ }),
+ queryParam({
+ name: 'extension',
+ example: 'js',
+ description: extensionDocs,
+ }),
+ ],
+ },
+ },
+ '/github/directory-file-count/{user}/{repo}/{path}': {
+ get: {
+ summary: 'GitHub repo file or directory count (in path)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'path', example: 'services' }),
+ queryParam({
+ name: 'type',
+ example: 'file',
+ schema: { type: 'string', enum: typeEnum },
+ description: typeDocs,
+ }),
+ queryParam({
+ name: 'extension',
+ example: 'js',
+ description: extensionDocs,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { color: 'blue', label: 'files' }
+
+ static render({ count }) {
+ return {
+ message: metric(count),
+ }
+ }
+
+ async fetch({ user, repo, path = '' }) {
+ const expression = `HEAD:${path}`
+ return this._requestGraphql({
+ query: gql`
+ query RepoFiles($user: String!, $repo: String!, $expression: String!) {
+ repository(owner: $user, name: $repo) {
+ object(expression: $expression) {
+ ... on Tree {
+ entries {
+ type
+ extension
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: { user, repo, expression },
+ schema,
+ transformErrors,
+ })
+ }
+
+ static transform(files, { type, extension }) {
+ if (!Array.isArray(files)) {
+ throw new InvalidParameter({ prettyMessage: 'not a directory' })
+ }
+
+ if (type !== 'file' && extension) {
+ throw new InvalidParameter({
+ prettyMessage: 'extension is applicable for type file only',
+ })
+ }
+
+ if (type) {
+ const objectType = type === 'dir' ? 'tree' : 'blob'
+ files = files.filter(file => file.type === objectType)
+ }
+
+ if (extension) {
+ files = files.filter(file => file.extension === `.${extension}`)
+ }
+
+ return {
+ count: files.length,
+ }
+ }
+
+ async handle({ user, repo, path }, { type, extension }) {
+ const json = await this.fetch({ user, repo, path })
+ if (json.data.repository.object === null) {
+ throw new InvalidParameter({
+ prettyMessage: 'directory not found',
+ })
+ }
+ const content = json.data.repository.object.entries
+ const { count } = this.constructor.transform(content, { type, extension })
+ return this.constructor.render({ count })
+ }
+}
diff --git a/services/github/github-directory-file-count.spec.js b/services/github/github-directory-file-count.spec.js
new file mode 100644
index 0000000000000..17c3afdd076f7
--- /dev/null
+++ b/services/github/github-directory-file-count.spec.js
@@ -0,0 +1,65 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidParameter } from '../index.js'
+import GithubDirectoryFileCount from './github-directory-file-count.service.js'
+
+describe('GithubDirectoryFileCount', function () {
+ const contents = [
+ { extension: '', type: 'tree' },
+ { extension: '', type: 'tree' },
+ { extension: '.js', type: 'blob' },
+ { extension: '.js', type: 'blob' },
+ { extension: '.txt', type: 'blob' },
+ { extension: '', type: 'commit' },
+ ]
+
+ test(GithubDirectoryFileCount.transform, () => {
+ given(contents, {
+ type: 'dir',
+ }).expect({
+ count: 2,
+ })
+ given(contents, {
+ type: 'file',
+ }).expect({
+ count: 3,
+ })
+ given(contents, {
+ type: 'file',
+ extension: 'js',
+ }).expect({
+ count: 2,
+ })
+ })
+
+ it('throws InvalidParameter on receiving an object as contents instead of an array', function () {
+ expect(() => GithubDirectoryFileCount.transform({}, {}))
+ .to.throw(InvalidParameter)
+ .with.property('prettyMessage', 'not a directory')
+ })
+
+ it('throws InvalidParameter on receiving type dir and extension', function () {
+ expect(() =>
+ GithubDirectoryFileCount.transform(contents, {
+ type: 'dir',
+ extension: 'js',
+ }),
+ )
+ .to.throw(InvalidParameter)
+ .with.property(
+ 'prettyMessage',
+ 'extension is applicable for type file only',
+ )
+ })
+
+ it('throws InvalidParameter on receiving no type and extension', function () {
+ expect(() =>
+ GithubDirectoryFileCount.transform(contents, { extension: 'js' }),
+ )
+ .to.throw(InvalidParameter)
+ .with.property(
+ 'prettyMessage',
+ 'extension is applicable for type file only',
+ )
+ })
+})
diff --git a/services/github/github-directory-file-count.tester.js b/services/github/github-directory-file-count.tester.js
new file mode 100644
index 0000000000000..77c1793994552
--- /dev/null
+++ b/services/github/github-directory-file-count.tester.js
@@ -0,0 +1,36 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('directory file count').get('/badges/shields.json').expectBadge({
+ label: 'files',
+ message: isMetric,
+})
+
+t.create('directory file count (custom path)')
+ .get('/badges/shields/services.json')
+ .expectBadge({
+ label: 'files',
+ message: isMetric,
+ })
+
+t.create('directory file count (repo not found)')
+ .get('/badges/not_existing_repository.json')
+ .expectBadge({
+ label: 'files',
+ message: 'repo not found',
+ })
+
+t.create('directory file count (directory not found)')
+ .get('/badges/shields/not_existing_directory.json')
+ .expectBadge({
+ label: 'files',
+ message: 'directory not found',
+ })
+
+t.create('directory file count (not a directory)')
+ .get('/badges/shields/package.json.json')
+ .expectBadge({
+ label: 'files',
+ message: 'not a directory',
+ })
diff --git a/services/github/github-discussions-custom-search.service.js b/services/github/github-discussions-custom-search.service.js
new file mode 100644
index 0000000000000..f116609a8bbde
--- /dev/null
+++ b/services/github/github-discussions-custom-search.service.js
@@ -0,0 +1,115 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
+
+const discussionsSearchDocs = `
+For a full list of available filters and allowed values,
+see GitHub's documentation on
+[Searching discussions](https://docs.github.com/en/search-github/searching-on-github/searching-discussions).
+`
+
+const discussionCountSchema = Joi.object({
+ data: Joi.object({
+ search: Joi.object({
+ discussionCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ query: Joi.string().required(),
+}).required()
+
+class BaseGithubDiscussionsSearch extends GithubAuthV4Service {
+ static category = 'other'
+ static defaultBadgeData = { label: 'query', color: 'informational' }
+
+ static render({ discussionCount }) {
+ return { message: metric(discussionCount) }
+ }
+
+ async fetch({ query }) {
+ const data = await this._requestGraphql({
+ query: gql`
+ query ($query: String!) {
+ search(query: $query, type: DISCUSSION) {
+ discussionCount
+ }
+ }
+ `,
+ variables: { query },
+ schema: discussionCountSchema,
+ transformErrors,
+ })
+ return data.data.search.discussionCount
+ }
+}
+
+class GithubDiscussionsSearch extends BaseGithubDiscussionsSearch {
+ static route = {
+ base: 'github',
+ pattern: 'discussions-search',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/discussions-search': {
+ get: {
+ summary: 'GitHub discussions custom search',
+ description: documentation,
+ parameters: [
+ queryParam({
+ name: 'query',
+ description: discussionsSearchDocs,
+ example: 'repo:badges/shields is:answered answered-by:chris48s',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ async handle(namedParams, { query }) {
+ const discussionCount = await this.fetch({ query })
+ return this.constructor.render({ discussionCount })
+ }
+}
+
+class GithubRepoDiscussionsSearch extends BaseGithubDiscussionsSearch {
+ static route = {
+ base: 'github',
+ pattern: 'discussions-search/:user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/discussions-search/{user}/{repo}': {
+ get: {
+ summary: 'GitHub discussions custom search in repo',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ queryParam({
+ name: 'query',
+ description: discussionsSearchDocs,
+ example: 'is:answered answered-by:chris48s',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ async handle({ user, repo }, { query }) {
+ query = `repo:${user}/${repo} ${query}`
+ const discussionCount = await this.fetch({ query })
+ return this.constructor.render({ discussionCount })
+ }
+}
+
+export { GithubDiscussionsSearch, GithubRepoDiscussionsSearch }
diff --git a/services/github/github-discussions-custom-search.tester.js b/services/github/github-discussions-custom-search.tester.js
new file mode 100644
index 0000000000000..213b69e4990b3
--- /dev/null
+++ b/services/github/github-discussions-custom-search.tester.js
@@ -0,0 +1,48 @@
+import { isMetric } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
+ id: 'GithubDiscussionsSearch',
+ title: 'Github Discussions Search',
+ pathPrefix: '/github',
+})
+
+t.create('GitHub discussions search (valid query string)')
+ .get(
+ '/discussions-search.json?query=repo%3Abadges%2Fshields%20is%3Aanswered%20author%3Achris48s',
+ )
+ .expectBadge({
+ label: 'query',
+ message: isMetric,
+ })
+
+t.create('GitHub discussions search (invalid query string)')
+ .get('/discussions-search.json?query=')
+ .expectBadge({
+ label: 'query',
+ message: 'invalid query parameter: query',
+ })
+
+t.create('GitHub Repo discussions search (valid query string)')
+ .get(
+ '/discussions-search/badges/shields.json?query=is%3Aanswered%20author%3Achris48s',
+ )
+ .expectBadge({
+ label: 'query',
+ message: isMetric,
+ })
+
+t.create('GitHub Repo discussions search (invalid query string)')
+ .get('/discussions-search/badges/shields.json?query=')
+ .expectBadge({
+ label: 'query',
+ message: 'invalid query parameter: query',
+ })
+
+t.create('GitHub Repo discussions search (invalid repo)')
+ .get(
+ '/discussions-search/badges/helmets.json?query=is%3Aanswered%20author%3Achris48s',
+ )
+ .expectBadge({
+ label: 'query',
+ message: '0',
+ })
diff --git a/services/github/github-discussions.service.js b/services/github/github-discussions.service.js
new file mode 100644
index 0000000000000..aa6b8d6fc91d4
--- /dev/null
+++ b/services/github/github-discussions.service.js
@@ -0,0 +1,100 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { transformErrors } from './github-helpers.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ discussions: Joi.object({
+ totalCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const variantConfig = {
+ all: { color: 'blue', suffix: 'total', answered: null },
+ answered: { color: 'brightgreen', suffix: 'answered', answered: true },
+ unanswered: { color: 'orange', suffix: 'unanswered', answered: false },
+}
+
+export default class GithubDiscussions extends GithubAuthV4Service {
+ static category = 'other'
+ static route = {
+ base: 'github/discussions',
+ pattern: ':variant(all|answered|unanswered)?/:user/:repo',
+ }
+
+ static openApi = {
+ '/github/discussions/all/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Discussions (all)',
+ parameters: [
+ pathParam({ name: 'user', example: 'vercel' }),
+ pathParam({ name: 'repo', example: 'next.js' }),
+ ],
+ },
+ },
+ '/github/discussions/answered/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Answered Discussions',
+ parameters: [
+ pathParam({ name: 'user', example: 'vercel' }),
+ pathParam({ name: 'repo', example: 'next.js' }),
+ ],
+ },
+ },
+ '/github/discussions/unanswered/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Unanswered Discussions',
+ parameters: [
+ pathParam({ name: 'user', example: 'vercel' }),
+ pathParam({ name: 'repo', example: 'next.js' }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'discussions', color: 'blue' }
+
+ static render({ discussions, color }) {
+ return { message: discussions, color }
+ }
+
+ async fetch({ user, repo, answered }) {
+ return this._requestGraphql({
+ query: gql`
+ query ($user: String!, $repo: String!, $answered: Boolean) {
+ repository(name: $repo, owner: $user) {
+ discussions(answered: $answered) {
+ totalCount
+ }
+ }
+ }
+ `,
+ variables: { user, repo, answered },
+ schema,
+ options: { headers: { 'GraphQL-Features': 'discussions_api' } },
+ transformErrors,
+ })
+ }
+
+ async handle({ user, repo, variant }) {
+ const { answered, color, suffix } = variantConfig[variant || 'all']
+ const json = await this.fetch({ user, repo, answered })
+ const {
+ data: {
+ repository: {
+ discussions: { totalCount },
+ },
+ },
+ } = json
+ return this.constructor.render({
+ discussions: `${totalCount} ${suffix}`,
+ color,
+ })
+ }
+}
diff --git a/services/github/github-discussions.tester.js b/services/github/github-discussions.tester.js
new file mode 100644
index 0000000000000..2b4aec00217d0
--- /dev/null
+++ b/services/github/github-discussions.tester.js
@@ -0,0 +1,31 @@
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('GitHub Discussions (repo not found)')
+ .get('/not-a-user/not-a-repo.json')
+ .expectBadge({ label: 'discussions', message: 'repo not found' })
+
+const numberSpaceTotal = withRegex(/^\d+ total$/)
+
+t.create('GitHub Discussions (repo having discussions)')
+ .get('/vercel/next.js.json')
+ .expectBadge({ label: 'discussions', message: numberSpaceTotal })
+
+t.create('GitHub Discussions all (repo having discussions)')
+ .get('/all/vercel/next.js.json')
+ .expectBadge({ label: 'discussions', message: numberSpaceTotal })
+
+t.create('GitHub Answered Discussions (repo having discussions)')
+ .get('/answered/vercel/next.js.json')
+ .expectBadge({
+ label: 'discussions',
+ message: withRegex(/^\d+ answered$/),
+ })
+
+t.create('GitHub Unanswered Discussions (repo having discussions)')
+ .get('/unanswered/vercel/next.js.json')
+ .expectBadge({
+ label: 'discussions',
+ message: withRegex(/^\d+ unanswered$/),
+ })
diff --git a/services/github/github-downloads.service.js b/services/github/github-downloads.service.js
index 5bd470ec85271..4b853358c02a9 100644
--- a/services/github/github-downloads.service.js
+++ b/services/github/github-downloads.service.js
@@ -1,12 +1,19 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { fetchLatestRelease } from './github-common-release.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { NotFound } = require('..')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+const sortEnum = ['date', 'semver']
+
+const queryParamSchema = Joi.object({
+ sort: Joi.string()
+ .valid(...sortEnum)
+ .default('date'),
+ displayAssetName: Joi.boolean().default(true),
+}).required()
const releaseSchema = Joi.object({
assets: Joi.array()
@@ -19,202 +26,182 @@ const releaseSchema = Joi.object({
const releaseArraySchema = Joi.alternatives().try(
Joi.array().items(releaseSchema),
- Joi.array().length(0)
+ Joi.array().length(0),
)
-module.exports = class GithubDownloads extends GithubAuthV3Service {
- static get category() {
- return 'downloads'
- }
+const variantParam = pathParam({
+ name: 'variant',
+ example: 'downloads',
+ description: 'downloads including or excluding pre-releases',
+ schema: { type: 'string', enum: ['downloads', 'downloads-pre'] },
+})
+const userParam = pathParam({ name: 'user', example: 'atom' })
+const repoParam = pathParam({ name: 'repo', example: 'atom' })
+const tagParam = pathParam({ name: 'tag', example: 'v0.190.0' })
+const assetNameParam = pathParam({
+ name: 'assetName',
+ example: 'atom-amd64.deb',
+})
+const displayAssetNameParam = queryParam({
+ name: 'displayAssetName',
+ example: 'false',
+ schema: { type: 'boolean' },
+ description: 'Whether to display the asset name in the badge value',
+})
+const sortParam = queryParam({
+ name: 'sort',
+ example: 'semver',
+ schema: { type: 'string', enum: sortEnum },
+ description: 'Method used to determine latest release. Default: `date`',
+})
- static get route() {
- return {
- base: 'github',
- pattern: ':kind(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
- }
+export default class GithubDownloads extends GithubAuthV3Service {
+ static category = 'downloads'
+ static route = {
+ base: 'github',
+ pattern: ':variant(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub All Releases',
- pattern: 'downloads/:user/:repo/total',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- },
- staticPreview: this.render({
- assetName: 'total',
- downloadCount: 857000,
- }),
- documentation,
+ static openApi = {
+ '/github/downloads/{user}/{repo}/total': {
+ get: {
+ summary: 'GitHub Downloads (all assets, all releases)',
+ description: documentation,
+ parameters: [userParam, repoParam],
},
- {
- title: 'GitHub Releases',
- pattern: 'downloads/:user/:repo/:tag/total',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- tag: 'latest',
- },
- staticPreview: this.render({
- tag: 'latest',
- assetName: 'total',
- downloadCount: 27000,
- }),
- documentation,
+ },
+ '/github/{variant}/{user}/{repo}/latest/total': {
+ get: {
+ summary: 'GitHub Downloads (all assets, latest release)',
+ description: documentation,
+ parameters: [variantParam, userParam, repoParam, sortParam],
},
- {
- title: 'GitHub Pre-Releases',
- pattern: 'downloads-pre/:user/:repo/:tag/total',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- tag: 'latest',
- },
- staticPreview: this.render({
- tag: 'latest',
- assetName: 'total',
- downloadCount: 2000,
- }),
- documentation,
+ },
+ '/github/downloads/{user}/{repo}/{tag}/total': {
+ get: {
+ summary: 'GitHub Downloads (all assets, specific tag)',
+ description: documentation,
+ parameters: [userParam, repoParam, tagParam],
},
- {
- title: 'GitHub Releases (by Release)',
- pattern: 'downloads/:user/:repo/:tag/total',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- tag: 'v0.190.0',
- },
- staticPreview: this.render({
- tag: 'v0.190.0',
- assetName: 'total',
- downloadCount: 490000,
- }),
- documentation,
+ },
+ '/github/downloads/{user}/{repo}/{assetName}': {
+ get: {
+ summary: 'GitHub Downloads (specific asset, all releases)',
+ description: documentation,
+ parameters: [
+ userParam,
+ repoParam,
+ assetNameParam,
+ displayAssetNameParam,
+ ],
},
- {
- title: 'GitHub Releases (by Asset)',
- pattern: 'downloads/:user/:repo/:tag/:path',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- tag: 'latest',
- path: 'atom-amd64.deb',
- },
- staticPreview: this.render({
- tag: 'latest',
- assetName: 'atom-amd64.deb',
- downloadCount: 3000,
- }),
- documentation,
+ },
+ '/github/{variant}/{user}/{repo}/latest/{assetName}': {
+ get: {
+ summary: 'GitHub Downloads (specific asset, latest release)',
+ description: documentation,
+ parameters: [
+ variantParam,
+ userParam,
+ repoParam,
+ assetNameParam,
+ displayAssetNameParam,
+ sortParam,
+ ],
},
- {
- title: 'GitHub Pre-Releases (by Asset)',
- pattern: 'downloads-pre/:user/:repo/:tag/:path',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- tag: 'latest',
- path: 'atom-amd64.deb',
- },
- staticPreview: this.render({
- tag: 'latest',
- assetName: 'atom-amd64.deb',
- downloadCount: 237,
- }),
- documentation,
+ },
+ '/github/downloads/{user}/{repo}/{tag}/{assetName}': {
+ get: {
+ summary: 'GitHub Downloads (specific asset, specific tag)',
+ description: documentation,
+ parameters: [
+ userParam,
+ repoParam,
+ tagParam,
+ assetNameParam,
+ displayAssetNameParam,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'downloads',
- namedLogo: 'github',
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
- static render({ tag, assetName, downloadCount }) {
- return {
- label: tag ? `downloads@${tag}` : 'downloads',
- message:
- assetName === 'total'
- ? metric(downloadCount)
- : `${metric(downloadCount)} [${assetName}]`,
- color: downloadCountColor(downloadCount),
- }
+ static render({
+ tag: version,
+ assetName,
+ downloads,
+ displayAssetName = true,
+ }) {
+ const messageSuffixOverride =
+ assetName !== 'total' && displayAssetName ? `[${assetName}]` : undefined
+ return renderDownloadsBadge({ downloads, messageSuffixOverride, version })
}
static transform({ releases, assetName }) {
- const downloadCount = releases.reduce((accum1, { assets }) => {
+ const downloads = releases.reduce((accum1, { assets }) => {
const filteredAssets =
assetName === 'total'
? assets
: assets.filter(
- ({ name }) => name.toLowerCase() === assetName.toLowerCase()
+ ({ name }) => name.toLowerCase() === assetName.toLowerCase(),
)
return (
accum1 +
filteredAssets.reduce(
- (accum2, { download_count: downloadCount }) => accum2 + downloadCount,
- 0
+ (accum2, { download_count: downloads }) => accum2 + downloads,
+ 0,
)
)
}, 0)
- return { downloadCount }
+ return { downloads }
}
- async handle({ kind, user, repo, tag, assetName }) {
+ async handle(
+ { variant, user, repo, tag, assetName },
+ { sort, displayAssetName },
+ ) {
let releases
- if (tag === 'latest' && kind === 'downloads') {
- const latestRelease = await this._requestJson({
- schema: releaseSchema,
- url: `/repos/${user}/${repo}/releases/latest`,
- errorMessages: errorMessagesFor('repo not found'),
- })
+ if (tag === 'latest') {
+ const includePre = variant === 'downloads-pre' || undefined
+ const latestRelease = await fetchLatestRelease(
+ this,
+ { user, repo },
+ { sort, include_prereleases: includePre },
+ )
releases = [latestRelease]
- } else if (tag === 'latest') {
- // Keep only the latest release.
- const [latestReleaseIncludingPrereleases] = await this._requestJson({
- schema: releaseArraySchema,
- url: `/repos/${user}/${repo}/releases`,
- options: { qs: { per_page: 1 } },
- errorMessages: errorMessagesFor('repo not found'),
- })
- // Note that the API will return an empty array if there are no releases
- // https://github.com/badges/shields/issues/3786
- if (!latestReleaseIncludingPrereleases) {
- throw new NotFound({ prettyMessage: 'no releases' })
- }
- releases = [latestReleaseIncludingPrereleases]
} else if (tag) {
const wantedRelease = await this._requestJson({
schema: releaseSchema,
url: `/repos/${user}/${repo}/releases/tags/${tag}`,
- errorMessages: errorMessagesFor('repo or release not found'),
+ httpErrors: httpErrorsFor('repo or release not found'),
})
releases = [wantedRelease]
} else {
const allReleases = await this._requestJson({
schema: releaseArraySchema,
url: `/repos/${user}/${repo}/releases`,
- options: { qs: { per_page: 500 } },
- errorMessages: errorMessagesFor('repo not found'),
+ options: { searchParams: { per_page: 500 } },
+ httpErrors: httpErrorsFor('repo not found'),
})
releases = allReleases
}
if (releases.length === 0) {
- throw new NotFound({ prettyMessage: 'no releases' })
+ throw new NotFound({ prettyMessage: 'no releases found' })
}
- const { downloadCount } = this.constructor.transform({
+ const { downloads } = this.constructor.transform({
releases,
assetName,
})
- return this.constructor.render({ tag, assetName, downloadCount })
+ return this.constructor.render({
+ tag,
+ assetName,
+ downloads,
+ displayAssetName,
+ })
}
}
diff --git a/services/github/github-downloads.tester.js b/services/github/github-downloads.tester.js
index f527d40617553..afbb442294f6f 100644
--- a/services/github/github-downloads.tester.js
+++ b/services/github/github-downloads.tester.js
@@ -1,8 +1,17 @@
-'use strict'
+import Joi from 'joi'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+const mockLatestRelease = release => nock =>
+ nock('https://api.github.com')
+ .get('/repos/photonstorm/phaser/releases/latest')
+ .reply(200, release)
+
+const mockReleases = releases => nock =>
+ nock('https://api.github.com')
+ .get('/repos/photonstorm/phaser/releases?per_page=100')
+ .reply(200, releases)
t.create('Downloads all releases')
.get('/downloads/photonstorm/phaser/total.json')
@@ -10,11 +19,11 @@ t.create('Downloads all releases')
t.create('Downloads all releases (no releases)')
.get('/downloads/badges/shields/total.json')
- .expectBadge({ label: 'downloads', message: 'no releases' })
+ .expectBadge({ label: 'downloads', message: 'no releases found' })
t.create('Downloads-pre all releases (no releases)')
.get('/downloads-pre/badges/shields/total.json')
- .expectBadge({ label: 'downloads', message: 'no releases' })
+ .expectBadge({ label: 'downloads', message: 'no releases found' })
t.create('Downloads all releases (repo not found)')
.get('/downloads/badges/helmets/total.json')
@@ -28,6 +37,116 @@ t.create('downloads for latest release')
.get('/downloads/photonstorm/phaser/latest/total.json')
.expectBadge({ label: 'downloads@latest', message: isMetric })
+t.create('downloads for latest release (sort by date)')
+ .get('/downloads/photonstorm/phaser/latest/total.json')
+ .intercept(
+ mockLatestRelease({
+ assets: [
+ { name: 'phaser.js', download_count: 5 },
+ { name: 'phaser.min.js', download_count: 7 },
+ ],
+ tag_name: 'v3.15.1',
+ prerelease: false,
+ }),
+ )
+ .expectBadge({ label: 'downloads@latest', message: '12' })
+
+t.create('downloads for latest release (sort by SemVer)')
+ .get('/downloads/photonstorm/phaser/latest/total.json?sort=semver')
+ .intercept(
+ mockReleases([
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 1 },
+ { name: 'phaser.min.js', download_count: 3 },
+ ],
+ tag_name: 'v3.16.0-rc1',
+ prerelease: true,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 5 },
+ { name: 'phaser.min.js', download_count: 7 },
+ ],
+ tag_name: 'v3.15.0',
+ prerelease: false,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 9 },
+ { name: 'phaser.min.js', download_count: 11 },
+ ],
+ tag_name: 'v3.15.1',
+ prerelease: false,
+ },
+ ]),
+ )
+ .expectBadge({ label: 'downloads@latest', message: '20' })
+
+t.create('downloads for latest release (sort by date including pre-releases)')
+ .get('/downloads-pre/photonstorm/phaser/latest/total.json')
+ .intercept(
+ mockReleases([
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 1 },
+ { name: 'phaser.min.js', download_count: 3 },
+ ],
+ tag_name: 'v3.16.0-rc1',
+ prerelease: true,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 5 },
+ { name: 'phaser.min.js', download_count: 7 },
+ ],
+ tag_name: 'v3.15.0',
+ prerelease: false,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 9 },
+ { name: 'phaser.min.js', download_count: 11 },
+ ],
+ tag_name: 'v3.15.1',
+ prerelease: false,
+ },
+ ]),
+ )
+ .expectBadge({ label: 'downloads@latest', message: '4' })
+
+t.create('downloads for latest release (sort by SemVer including pre-releases)')
+ .get('/downloads-pre/photonstorm/phaser/latest/total.json?sort=semver')
+ .intercept(
+ mockReleases([
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 9 },
+ { name: 'phaser.min.js', download_count: 11 },
+ ],
+ tag_name: 'v3.15.1',
+ prerelease: false,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 1 },
+ { name: 'phaser.min.js', download_count: 3 },
+ ],
+ tag_name: 'v3.16.0-rc1',
+ prerelease: true,
+ },
+ {
+ assets: [
+ { name: 'phaser.js', download_count: 5 },
+ { name: 'phaser.min.js', download_count: 7 },
+ ],
+ tag_name: 'v3.15.0',
+ prerelease: false,
+ },
+ ]),
+ )
+ .expectBadge({ label: 'downloads@latest', message: '4' })
+
t.create('downloads-pre for latest release')
.get('/downloads-pre/photonstorm/phaser/latest/total.json')
.expectBadge({ label: 'downloads@latest', message: isMetric })
@@ -35,7 +154,7 @@ t.create('downloads-pre for latest release')
// https://github.com/badges/shields/issues/3786
t.create('downloads-pre for latest release (no-releases)')
.get('/downloads-pre/badges/shields/latest/total.json')
- .expectBadge({ label: 'downloads', message: 'no releases' })
+ .expectBadge({ label: 'downloads', message: 'no releases found' })
t.create('downloads for release without slash')
.get('/downloads/atom/atom/v0.190.0/total.json')
@@ -46,7 +165,7 @@ t.create('downloads for specific asset without slash')
.expectBadge({
label: 'downloads@v0.190.0',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/,
),
})
@@ -55,19 +174,31 @@ t.create('downloads for specific asset from latest release')
.expectBadge({
label: 'downloads@latest',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/,
),
})
+t.create('downloads for specific asset from latest release without asset name')
+ .get('/downloads/atom/atom/latest/atom-amd64.deb.json?displayAssetName=false')
+ .expectBadge({ label: 'downloads@latest', message: isMetric })
+
t.create('downloads-pre for specific asset from latest release')
.get('/downloads-pre/atom/atom/latest/atom-amd64.deb.json')
.expectBadge({
label: 'downloads@latest',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/,
),
})
+t.create(
+ 'downloads-pre for specific asset from latest release without asset name',
+)
+ .get(
+ '/downloads-pre/atom/atom/latest/atom-amd64.deb.json?displayAssetName=false',
+ )
+ .expectBadge({ label: 'downloads@latest', message: isMetric })
+
t.create('downloads for release with slash')
.get('/downloads/NHellFire/dban/stable/v2.2.8/total.json')
.expectBadge({ label: 'downloads@stable/v2.2.8', message: isMetric })
@@ -77,10 +208,16 @@ t.create('downloads for specific asset with slash')
.expectBadge({
label: 'downloads@stable/v2.2.8',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[dban-2\.2\.8_i586\.iso\]$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[dban-2\.2\.8_i586\.iso\]$/,
),
})
+t.create('downloads for specific asset with slash without asset name')
+ .get(
+ '/downloads/NHellFire/dban/stable/v2.2.8/dban-2.2.8_i586.iso.json?displayAssetName=false',
+ )
+ .expectBadge({ label: 'downloads@stable/v2.2.8', message: isMetric })
+
t.create('downloads for unknown release')
.get('/downloads/atom/atom/does-not-exist/total.json')
.expectBadge({ label: 'downloads', message: 'repo or release not found' })
diff --git a/services/github/github-followers.service.js b/services/github/github-followers.service.js
index f3c72055c2076..9336ba0dd9891 100644
--- a/services/github/github-followers.service.js
+++ b/services/github/github-followers.service.js
@@ -1,53 +1,35 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
followers: nonNegativeInteger,
}).required()
-module.exports = class GithubFollowers extends GithubAuthV3Service {
- static get category() {
- return 'social'
- }
-
- static get route() {
- return {
- base: 'github/followers',
- pattern: ':user',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub followers',
- namedParams: { user: 'espadrine' },
- staticPreview: Object.assign(this.render({ followers: 150 }), {
- label: 'Follow',
- style: 'social',
- }),
- queryParams: { label: 'Follow' },
- documentation,
+export default class GithubFollowers extends GithubAuthV3Service {
+ static category = 'social'
+ static route = { base: 'github/followers', pattern: ':user' }
+ static openApi = {
+ '/github/followers/{user}': {
+ get: {
+ summary: 'GitHub followers',
+ description: documentation,
+ parameters: pathParams({ name: 'user', example: 'espadrine' }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'followers',
- namedLogo: 'github',
- }
- }
+ static defaultBadgeData = { label: 'followers', namedLogo: 'github' }
- static render({ followers }) {
+ static render({ followers, user }) {
return {
message: metric(followers),
- color: '4183C4',
+ style: 'social',
+ color: 'blue',
+ link: [`https://github.com/${user}?tab=followers`],
}
}
@@ -55,8 +37,8 @@ module.exports = class GithubFollowers extends GithubAuthV3Service {
const { followers } = await this._requestJson({
url: `/users/${user}`,
schema,
- errorMessages: errorMessagesFor('user not found'),
+ httpErrors: httpErrorsFor('user not found'),
})
- return this.constructor.render({ followers })
+ return this.constructor.render({ followers, user })
}
}
diff --git a/services/github/github-followers.tester.js b/services/github/github-followers.tester.js
index 8ad5d8dbb2925..97c5844bc2ba5 100644
--- a/services/github/github-followers.tester.js
+++ b/services/github/github-followers.tester.js
@@ -1,18 +1,17 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Followers')
.get('/webcaetano.json')
.expectBadge({
label: 'followers',
message: isMetric,
+ color: 'blue',
+ link: ['https://github.com/webcaetano?tab=followers'],
})
-t.create('Followers (user not found)')
- .get('/PyvesB2.json')
- .expectBadge({
- label: 'followers',
- message: 'user not found',
- })
+t.create('Followers (user not found)').get('/PyvesB2.json').expectBadge({
+ label: 'followers',
+ message: 'user not found',
+})
diff --git a/services/github/github-forks.service.js b/services/github/github-forks.service.js
index 97a9bcae49b58..143b700119a92 100644
--- a/services/github/github-forks.service.js
+++ b/services/github/github-forks.service.js
@@ -1,11 +1,10 @@
-'use strict'
-
-const gql = require('graphql-tag')
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV4Service } = require('./github-auth-service')
-const { documentation, transformErrors } = require('./github-helpers')
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
const schema = Joi.object({
data: Joi.object({
@@ -15,55 +14,29 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class GithubForks extends GithubAuthV4Service {
- static get category() {
- return 'social'
- }
-
- static get route() {
- return {
- base: 'github/forks',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub forks',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- // TODO: This is currently a literal, as `staticPreview` doesn't
- // support `link`.
- staticPreview: {
- label: 'Fork',
- message: '150',
- style: 'social',
- },
- // staticPreview: {
- // ...this.render({ user: 'badges', repo: 'shields', forkCount: 150 }),
- // label: 'fork',
- // style: 'social',
- // },
- queryParams: { label: 'Fork' },
- documentation,
+export default class GithubForks extends GithubAuthV4Service {
+ static category = 'social'
+ static route = { base: 'github/forks', pattern: ':user/:repo' }
+ static openApi = {
+ '/github/forks/{user}/{repo}': {
+ get: {
+ summary: 'GitHub forks',
+ description: documentation,
+ parameters: pathParams(
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'forks',
- namedLogo: 'github',
- }
- }
+ static defaultBadgeData = { label: 'forks', namedLogo: 'github' }
static render({ user, repo, forkCount }) {
return {
message: metric(forkCount),
- color: '4183C4',
+ style: 'social',
+ color: 'blue',
link: [
`https://github.com/${user}/${repo}/fork`,
`https://github.com/${user}/${repo}/network`,
@@ -74,7 +47,7 @@ module.exports = class GithubForks extends GithubAuthV4Service {
async handle({ user, repo }) {
const json = await this._requestGraphql({
query: gql`
- query($user: String!, $repo: String!) {
+ query ($user: String!, $repo: String!) {
repository(owner: $user, name: $repo) {
forkCount
}
diff --git a/services/github/github-forks.tester.js b/services/github/github-forks.tester.js
index 4b733328740c8..31ad83fbbf8c7 100644
--- a/services/github/github-forks.tester.js
+++ b/services/github/github-forks.tester.js
@@ -1,22 +1,20 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Forks')
.get('/badges/shields.json')
.expectBadge({
label: 'forks',
message: isMetric,
+ color: 'blue',
link: [
'https://github.com/badges/shields/fork',
'https://github.com/badges/shields/network',
],
})
-t.create('Forks (repo not found)')
- .get('/badges/helmets.json')
- .expectBadge({
- label: 'forks',
- message: 'repo not found',
- })
+t.create('Forks (repo not found)').get('/badges/helmets.json').expectBadge({
+ label: 'forks',
+ message: 'repo not found',
+})
diff --git a/services/github/github-go-mod.service.js b/services/github/github-go-mod.service.js
index f958299cb6768..937f5f0e4f7dd 100644
--- a/services/github/github-go-mod.service.js
+++ b/services/github/github-go-mod.service.js
@@ -1,79 +1,62 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { InvalidResponse } = require('..')
-const { ConditionalGithubAuthV3Service } = require('./github-auth-service')
-const { fetchRepoContent } = require('./github-common-fetch')
-const { documentation } = require('./github-helpers')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchRepoContent } from './github-common-fetch.js'
+import { documentation } from './github-helpers.js'
const queryParamSchema = Joi.object({
filename: Joi.string(),
}).required()
-const goVersionRegExp = new RegExp('^go (.+)$', 'm')
-
-const keywords = ['golang']
+const goVersionRegExp = /^go ([^/\s]+)(\s*\/.+)?$/m
-module.exports = class GithubGoModGoVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'version'
- }
+const filenameDescription =
+ 'The `filename` param can be used to specify the path to `go.mod`. By default, we look for `go.mod` in the repo root'
- static get route() {
- return {
- base: 'github/go-mod/go-version',
- pattern: ':user/:repo/:branch*',
- queryParamSchema,
- }
+export default class GithubGoModGoVersion extends ConditionalGithubAuthV3Service {
+ static category = 'platform-support'
+ static route = {
+ base: 'github/go-mod/go-version',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub go.mod Go version',
- pattern: ':user/:repo',
- namedParams: { user: 'gohugoio', repo: 'hugo' },
- staticPreview: this.render({ version: '1.12' }),
- documentation,
- keywords,
- },
- {
- title: 'GitHub go.mod Go version (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'gohugoio',
- repo: 'hugo',
- branch: 'master',
- },
- staticPreview: this.render({ version: '1.12', branch: 'master' }),
- documentation,
- keywords,
+ static openApi = {
+ '/github/go-mod/go-version/{user}/{repo}': {
+ get: {
+ summary: 'GitHub go.mod Go version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'gohugoio' }),
+ pathParam({ name: 'repo', example: 'hugo' }),
+ queryParam({
+ name: 'filename',
+ example: 'src/go.mod',
+ description: filenameDescription,
+ }),
+ ],
},
- {
- title: 'GitHub go.mod Go version (subfolder of monorepo)',
- pattern: ':user/:repo',
- namedParams: { user: 'golang', repo: 'go' },
- queryParams: { filename: 'src/go.mod' },
- staticPreview: this.render({ version: '1.14' }),
- documentation,
- keywords,
+ },
+ '/github/go-mod/go-version/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub go.mod Go version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'gohugoio' }),
+ pathParam({ name: 'repo', example: 'hugo' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'filename',
+ example: 'src/go.mod',
+ description: filenameDescription,
+ }),
+ ],
},
- {
- title: 'GitHub go.mod Go version (branch & subfolder of monorepo)',
- pattern: ':user/:repo/:branch',
- namedParams: { user: 'golang', repo: 'go', branch: 'master' },
- queryParams: { filename: 'src/go.mod' },
- staticPreview: this.render({ version: '1.14' }),
- documentation,
- keywords,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'Go' }
- }
+ static defaultBadgeData = { label: 'Go' }
static render({ version, branch }) {
return renderVersionBadge({
diff --git a/services/github/github-go-mod.spec.js b/services/github/github-go-mod.spec.js
new file mode 100644
index 0000000000000..cec2276509d80
--- /dev/null
+++ b/services/github/github-go-mod.spec.js
@@ -0,0 +1,26 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import GithubGoModGoVersion from './github-go-mod.service.js'
+
+describe('GithubGoModGoVersion', function () {
+ describe('valid cases', function () {
+ test(GithubGoModGoVersion.transform, () => {
+ given('go 1.18').expect({ go: '1.18' })
+ given('go 1.18 // inline comment').expect({ go: '1.18' })
+ given('go 1.18// inline comment').expect({ go: '1.18' })
+ given('go 1.18 /* block comment */').expect({ go: '1.18' })
+ given('go 1.18/* block comment */').expect({ go: '1.18' })
+ given('go 1').expect({ go: '1' })
+ given('go 1.2.3').expect({ go: '1.2.3' })
+ given('go string').expect({ go: 'string' })
+ })
+ })
+
+ describe('invalid cases', function () {
+ expect(() => GithubGoModGoVersion.transform('')).to.throw(InvalidResponse)
+ expect(() =>
+ GithubGoModGoVersion.transform("doesn't start with go"),
+ ).to.throw(InvalidResponse)
+ })
+})
diff --git a/services/github/github-go-mod.tester.js b/services/github/github-go-mod.tester.js
index 9780d6c88ce8a..d42378079cc10 100644
--- a/services/github/github-go-mod.tester.js
+++ b/services/github/github-go-mod.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Go version')
- .get('/gohugoio/hugo.json')
- .expectBadge({
- label: 'Go',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('Go version').get('/gohugoio/hugo.json').expectBadge({
+ label: 'Go',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('Go version (from branch)')
.get('/gohugoio/hugo/master.json')
@@ -17,7 +14,7 @@ t.create('Go version (from branch)')
message: isVPlusDottedVersionAtLeastOne,
})
-t.create('Go version (mongorepo)')
+t.create('Go version (monorepo)')
.get(`/golang/go.json?filename=${encodeURIComponent('src/go.mod')}`)
.expectBadge({
label: 'Go',
diff --git a/services/github/github-hacktoberfest.service.js b/services/github/github-hacktoberfest.service.js
index 0192793a39047..a5766aab671e6 100644
--- a/services/github/github-hacktoberfest.service.js
+++ b/services/github/github-hacktoberfest.service.js
@@ -1,44 +1,35 @@
-'use strict'
-
-const gql = require('graphql-tag')
-const Joi = require('@hapi/joi')
-const moment = require('moment')
-const { metric, maybePluralize } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV4Service } = require('./github-auth-service')
-const {
- documentation: githubDocumentation,
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import dayjs from 'dayjs'
+import { pathParam, queryParam } from '../index.js'
+import { parseDate } from '../date.js'
+import { metric, maybePluralize } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import {
+ documentation as githubDocumentation,
transformErrors,
-} = require('./github-helpers')
-
-const documentation = `
-
- This badge is designed for projects hosted on GitHub which are
- participating in
- Hacktoberfest ,
- an initiative to encourage participating in open-source projects. The
- badge can be added to the project readme to encourage potential
- contributors to review the suggested issues and to celebrate the
- contributions that have already been made.
+} from './github-helpers.js'
- The badge displays three pieces of information:
-
-
- The number of suggested issues. By default this will count open
- issues with the hacktoberfest label, however you
- can pick a different label (e.g.
- ?suggestion_label=good%20first%20issue).
-
-
- The number of pull requests opened in October. This excludes any
- PR with the invalid label.
-
- The number of days left of October.
-
+const description = `
+This badge is designed for projects hosted on GitHub which are
+participating in
+[Hacktoberfest](https://hacktoberfest.digitalocean.com),
+an initiative to encourage participating in open-source projects. The
+badge can be added to the project readme to encourage potential
+contributors to review the suggested issues and to celebrate the
+contributions that have already been made.
+The badge displays three pieces of information:
-
+- The number of suggested issues. By default this will count open
+ issues with the hacktoberfest label, however you
+ can pick a different label (e.g.
+ \`?suggestion_label=good%20first%20issue\`).
+- The number of pull requests opened in October. This excludes any
+ PR with the invalid label.
+- The number of days left of October.
- ${githubDocumentation}
+${githubDocumentation}
`
const schema = Joi.object({
@@ -58,73 +49,62 @@ const queryParamSchema = Joi.object({
suggestion_label: Joi.string(),
}).required()
-module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Service {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'github/hacktoberfest/2019',
- pattern: ':user/:repo',
- queryParamSchema,
- }
+export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github/hacktoberfest',
+ pattern: ':year(2019|2020|2021|2022|2023|2024|2025)/:user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub Hacktoberfest combined status',
- namedParams: {
- user: 'snyk',
- repo: 'snyk',
- },
- staticPreview: this.render({
- suggestedIssueCount: 12,
- contributionCount: 8,
- daysLeft: 15,
- }),
- documentation,
+ static openApi = {
+ '/github/hacktoberfest/{year}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Hacktoberfest combined status',
+ description,
+ parameters: [
+ pathParam({
+ name: 'year',
+ example: '2025',
+ schema: { type: 'string', enum: this.getEnum('year') },
+ }),
+ pathParam({ name: 'user', example: 'tmrowco' }),
+ pathParam({ name: 'repo', example: 'tmrowapp-contrib' }),
+ queryParam({ name: 'suggestion_label', example: 'help wanted' }),
+ ],
},
- {
- title:
- 'GitHub Hacktoberfest combined status (suggestion label override)',
- namedParams: {
- user: 'tmrowco',
- repo: 'tmrowapp-contrib',
- },
- queryParams: {
- suggestion_label: 'help wanted',
- },
- staticPreview: this.render({
- suggestedIssueCount: 12,
- contributionCount: 8,
- daysLeft: 15,
- }),
- documentation,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'hacktoberfest',
- color: 'orange',
- }
- }
+ static defaultBadgeData = { label: 'hacktoberfest', color: 'orange' }
- static render({ suggestedIssueCount, contributionCount, daysLeft }) {
+ static render({
+ suggestedIssueCount,
+ contributionCount,
+ daysLeft,
+ daysToStart,
+ year,
+ hasStarted = true,
+ }) {
+ if (!hasStarted) {
+ return {
+ message: `${daysToStart} ${maybePluralize(
+ 'day',
+ daysToStart,
+ )} till kickoff!`,
+ }
+ }
if (daysLeft === undefined) {
// The global cutoff time is 11/1 noon UTC.
// https://github.com/badges/shields/pull/4109#discussion_r330782093
// We want to show "1 day left" on the last day so we add 1.
- daysLeft = moment('2019-11-01 12:00:00 Z').diff(moment(), 'days') + 1
+ daysLeft = parseDate(`${year}-11-01 12:00:00 Z`).diff(dayjs(), 'days') + 1
}
if (daysLeft < 0) {
return {
message: `is over! (${metric(contributionCount)} ${maybePluralize(
'PR',
- contributionCount
+ contributionCount,
)} opened)`,
}
}
@@ -133,13 +113,13 @@ module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Ser
suggestedIssueCount
? `${metric(suggestedIssueCount)} ${maybePluralize(
'open issue',
- suggestedIssueCount
+ suggestedIssueCount,
)}`
: '',
contributionCount
? `${metric(contributionCount)} ${maybePluralize(
'PR',
- contributionCount
+ contributionCount,
)}`
: '',
daysLeft > 0
@@ -152,12 +132,12 @@ module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Ser
return { message }
}
- async fetch({ user, repo, suggestionLabel = 'hacktoberfest' }) {
+ async fetch({ user, repo, year, suggestionLabel = 'hacktoberfest' }) {
const isValidOctoberPR = [
`repo:${user}/${repo}`,
'is:pr',
- 'created:2019-10-01..2019-10-31',
- `-label:invalid`,
+ `created:${year}-10-01..${year}-10-31`,
+ '-label:invalid',
]
.filter(Boolean)
.join(' ')
@@ -170,7 +150,7 @@ module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Ser
},
} = await this._requestGraphql({
query: gql`
- query(
+ query (
$user: String!
$repo: String!
$suggestionLabel: String!
@@ -201,15 +181,32 @@ module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Ser
}
}
- async handle({ user, repo }, { suggestion_label: suggestionLabel }) {
+ static getCalendarPosition(year) {
+ const daysToStart = parseDate(`${year}-10-01 00:00:00 Z`).diff(
+ dayjs(),
+ 'days',
+ )
+ const isBefore = daysToStart > 0
+ return { daysToStart, isBefore }
+ }
+
+ async handle({ user, repo, year }, { suggestion_label: suggestionLabel }) {
+ const { isBefore, daysToStart } =
+ this.constructor.getCalendarPosition(+year)
+ if (isBefore) {
+ return this.constructor.render({ hasStarted: false, daysToStart, year })
+ }
+
const { suggestedIssueCount, contributionCount } = await this.fetch({
user,
repo,
+ year,
suggestionLabel,
})
return this.constructor.render({
suggestedIssueCount,
contributionCount,
+ year,
})
}
}
diff --git a/services/github/github-hacktoberfest.spec.js b/services/github/github-hacktoberfest.spec.js
index b62bbe765a61b..c872f7ab64bf7 100644
--- a/services/github/github-hacktoberfest.spec.js
+++ b/services/github/github-hacktoberfest.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import GitHubHacktoberfest from './github-hacktoberfest.service.js'
-const { test, given } = require('sazerac')
-const GitHubHacktoberfest = require('./github-hacktoberfest.service')
-
-describe('GitHubHacktoberfest', function() {
+describe('GitHubHacktoberfest', function () {
test(GitHubHacktoberfest.render, () => {
given({
daysLeft: -1,
@@ -18,5 +16,12 @@ describe('GitHubHacktoberfest', function() {
}).expect({
message: '54 open issues, 27 PRs, 10 days left',
})
+ given({
+ daysToStart: 5,
+ hasStarted: false,
+ year: 2020,
+ }).expect({
+ message: '5 days till kickoff!',
+ })
})
})
diff --git a/services/github/github-hacktoberfest.tester.js b/services/github/github-hacktoberfest.tester.js
index 51eb74c164840..6192fee41ceeb 100644
--- a/services/github/github-hacktoberfest.tester.js
+++ b/services/github/github-hacktoberfest.tester.js
@@ -1,26 +1,25 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isHacktoberfestNoIssuesStatus = Joi.string().regex(
- /^[0-9]+ PRs?(, [0-9]+ days? left)?$/
+ /^[0-9]+ PRs?(, [0-9]+ days? left)?$/,
)
const isHacktoberfestNoPRsStatus = Joi.string().regex(
- /^([0-9]+ open issues?)?[0-9]+ days? left$/
+ /^([0-9]+ open issues?)?[0-9]+ days? left$/,
)
const isHacktoberfestCombinedStatus = Joi.string().regex(
- /^[0-9]+ open issues?(, [0-9]+ PRs?)?(, [0-9]+ days? left)?$/
+ /^[0-9]+ open issues?(, [0-9]+ PRs?)?(, [0-9]+ days? left)?$/,
)
const isHacktoberfestStatus = Joi.alternatives().try(
isHacktoberfestNoIssuesStatus,
isHacktoberfestNoPRsStatus,
isHacktoberfestCombinedStatus,
- /^is over! \([0-9]+ PRs? opened\)$/
+ /^is over! \([0-9]+ PRs? opened\)$/,
)
t.create('GitHub Hacktoberfest combined status')
- .get('/badges/shields.json')
+ .get('/2019/badges/shields.json')
.expectBadge({
label: 'hacktoberfest',
message: isHacktoberfestStatus,
@@ -28,9 +27,9 @@ t.create('GitHub Hacktoberfest combined status')
t.create('GitHub Hacktoberfest combined status (suggestion label override)')
.get(
- `/badges/shields.json?suggestion_label=${encodeURIComponent(
- 'good first issue'
- )}`
+ `/2019/badges/shields.json?suggestion_label=${encodeURIComponent(
+ 'good first issue',
+ )}`,
)
.expectBadge({
label: 'hacktoberfest',
diff --git a/services/github/github-helpers.js b/services/github/github-helpers.js
index edf44ce9c7646..97beda1fd1cf5 100644
--- a/services/github/github-helpers.js
+++ b/services/github/github-helpers.js
@@ -1,32 +1,31 @@
-'use strict'
-
-const serverSecrets = require('../../lib/server-secrets')
-const { colorScale } = require('../color-formatters')
-const { InvalidResponse, NotFound } = require('..')
+import { colorScale } from '../color-formatters.js'
+import { InvalidResponse, NotFound } from '../index.js'
const documentation = `
-
- If your GitHub badge errors, it might be because you hit GitHub's rate limits.
- You can increase Shields.io's rate limit by
- adding the Shields GitHub
- application using your GitHub account.
-
+You can help increase Shields.io's rate limit by
+[authorizing the Shields.io GitHub application](https://img.shields.io/github-auth).
+Read more about [how it works](/blog/token-pool).
`
-function stateColor(s) {
- return { open: '2cbe4e', closed: 'cb2431', merged: '6f42c1' }[s]
+function issueStateColor(s) {
+ return {
+ open: '2cbe4e',
+ closed: '6f42c1',
+ 'not planned': '666c76',
+ duplicate: '666c76',
+ }[s]
}
-function errorMessagesFor(notFoundMessage = 'repo not found') {
+function httpErrorsFor(notFoundMessage = 'repo not found') {
return {
404: notFoundMessage,
422: notFoundMessage,
}
}
-function transformErrors(errors) {
+function transformErrors(errors, entity = 'repo') {
if (errors[0].type === 'NOT_FOUND') {
- return new NotFound({ prettyMessage: 'repo not found' })
+ return new NotFound({ prettyMessage: `${entity} not found` })
} else {
return new InvalidResponse({ prettyMessage: errors[0].message })
}
@@ -34,15 +33,10 @@ function transformErrors(errors) {
const commentsColor = colorScale([1, 3, 10, 25], undefined, true)
-function staticAuthConfigured() {
- return Boolean(serverSecrets.gh_token)
-}
-
-module.exports = {
+export {
documentation,
- stateColor,
+ issueStateColor,
commentsColor,
- errorMessagesFor,
+ httpErrorsFor,
transformErrors,
- staticAuthConfigured,
}
diff --git a/services/github/github-issue-detail-redirect.service.js b/services/github/github-issue-detail-redirect.service.js
index 40be57b51b99c..4e56b30620119 100644
--- a/services/github/github-issue-detail-redirect.service.js
+++ b/services/github/github-issue-detail-redirect.service.js
@@ -1,22 +1,15 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-const variantMap = {
- s: 'state',
- u: 'author',
-}
-
-module.exports = [
- redirector({
+export default [
+ retiredService({
category: 'issue-tracking',
+ label: 'github',
route: {
base: 'github',
pattern:
':issueKind(issues|pulls)/detail/:variant(s|u)/:user/:repo/:number([0-9]+)',
},
- transformPath: ({ issueKind, variant, user, repo, number }) =>
- `/github/${issueKind}/detail/${variantMap[variant]}/${user}/${repo}/${number}`,
- dateAdded: new Date('2019-04-04'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
]
diff --git a/services/github/github-issue-detail-redirect.tester.js b/services/github/github-issue-detail-redirect.tester.js
index da07a159e6f0c..2ad24d5319e7f 100644
--- a/services/github/github-issue-detail-redirect.tester.js
+++ b/services/github/github-issue-detail-redirect.tester.js
@@ -1,25 +1,35 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubIssueDetailRedirect',
title: 'GithubIssueDetailRedirect',
pathPrefix: '/github',
-}))
+})
t.create('github issue detail (s shorthand)')
- .get('/issues/detail/s/badges/shields/979.svg')
- .expectRedirect('/github/issues/detail/state/badges/shields/979.svg')
+ .get('/issues/detail/s/badges/shields/979.json')
+ .expectBadge({
+ label: 'github',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('github issue detail (u shorthand)')
- .get('/issues/detail/u/badges/shields/979.svg')
- .expectRedirect('/github/issues/detail/author/badges/shields/979.svg')
+ .get('/issues/detail/u/badges/shields/979.json')
+ .expectBadge({
+ label: 'github',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('github pulls detail (s shorthand)')
- .get('/pulls/detail/s/badges/shields/979.svg')
- .expectRedirect('/github/pulls/detail/state/badges/shields/979.svg')
+ .get('/pulls/detail/s/badges/shields/979.json')
+ .expectBadge({
+ label: 'github',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('github pulls detail (u shorthand)')
- .get('/pulls/detail/u/badges/shields/979.svg')
- .expectRedirect('/github/pulls/detail/author/badges/shields/979.svg')
+ .get('/pulls/detail/u/badges/shields/979.json')
+ .expectBadge({
+ label: 'github',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/github/github-issue-detail.service.js b/services/github/github-issue-detail.service.js
index 145028a46c0de..8289fa056fdbc 100644
--- a/services/github/github-issue-detail.service.js
+++ b/services/github/github-issue-detail.service.js
@@ -1,17 +1,15 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { formatDate, metric } = require('../text-formatters')
-const { age } = require('../color-formatters')
-const { InvalidResponse } = require('..')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const {
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { renderDateBadge } from '../date.js'
+import { InvalidResponse, pathParams } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
documentation,
- errorMessagesFor,
- stateColor,
+ httpErrorsFor,
+ issueStateColor,
commentsColor,
-} = require('./github-helpers')
+} from './github-helpers.js'
const commonSchemaFields = {
number: nonNegativeInteger,
@@ -21,23 +19,29 @@ const commonSchemaFields = {
const stateMap = {
schema: Joi.object({
...commonSchemaFields,
- state: Joi.string()
- .allow('open', 'closed')
- .required(),
+ state: Joi.equal('open', 'closed').required(),
+ state_reason: Joi.string().allow(null), // only for issues
merged_at: Joi.string().allow(null),
}).required(),
- transform: ({ json }) => ({
- state: json.state,
- // Because eslint will not be happy with this snake_case name :(
- merged: json.merged_at !== null,
- }),
+ transform: ({ json }) => {
+ const mergedAt = json.pull_request?.merged_at ?? json.merged_at
+ const stateWithReason =
+ json.state_reason === 'not_planned' || json.state_reason === 'duplicate'
+ ? json.state_reason.replace('_', ' ')
+ : json.state
+
+ return {
+ state: stateWithReason,
+ merged: mergedAt != null,
+ }
+ },
render: ({ value, isPR, number }) => {
const state = value.state
const label = `${isPR ? 'pull request' : 'issue'} ${number}`
if (!isPR || state === 'open') {
return {
- color: stateColor(state),
+ color: issueStateColor(state),
label,
message: state,
}
@@ -90,7 +94,7 @@ const labelMap = {
Joi.object({
name: Joi.string().required(),
color: Joi.string().required(),
- })
+ }),
)
.required(),
}).required(),
@@ -137,10 +141,32 @@ const ageUpdateMap = {
}).required(),
transform: ({ json, property }) =>
property === 'age' ? json.created_at : json.updated_at,
- render: ({ property, value }) => ({
- color: age(value),
- label: property === 'age' ? 'created' : 'updated',
- message: formatDate(value),
+ render: ({ property, value }) => {
+ const label = property === 'age' ? 'created' : 'updated'
+ return {
+ ...renderDateBadge(value),
+ label,
+ }
+ },
+}
+
+const milestoneMap = {
+ schema: Joi.object({
+ ...commonSchemaFields,
+ milestone: Joi.object({
+ title: Joi.string().required(),
+ }).allow(null),
+ }).required(),
+ transform: ({ json }) => {
+ if (!json.milestone) {
+ throw new InvalidResponse({ prettyMessage: 'no milestone' })
+ }
+ return json.milestone.title
+ },
+ render: ({ value }) => ({
+ label: 'milestone',
+ message: value,
+ color: 'informational',
}),
}
@@ -152,57 +178,53 @@ const propertyMap = {
comments: commentsMap,
age: ageUpdateMap,
'last-update': ageUpdateMap,
+ milestone: milestoneMap,
}
-module.exports = class GithubIssueDetail extends GithubAuthV3Service {
- static get category() {
- return 'issue-tracking'
+export default class GithubIssueDetail extends GithubAuthV3Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github',
+ pattern:
+ ':issueKind(issues|pulls)/detail/:property(state|title|author|label|comments|age|last-update|milestone)/:user/:repo/:number([0-9]+)',
}
- static get route() {
- return {
- base: 'github',
- pattern:
- ':issueKind(issues|pulls)/detail/:property(state|title|author|label|comments|age|last-update)/:user/:repo/:number([0-9]+)',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub issue/pull request detail',
- namedParams: {
- issueKind: 'issues',
- property: 'state',
- user: 'badges',
- repo: 'shields',
- number: '979',
- },
- staticPreview: this.render({
- property: 'state',
- value: { state: 'closed' },
- isPR: false,
- number: '979',
- }),
- keywords: [
- 'state',
- 'title',
- 'author',
- 'label',
- 'comments',
- 'age',
- 'last update',
- ],
- documentation,
+ static openApi = {
+ '/github/{issueKind}/detail/{property}/{user}/{repo}/{number}': {
+ get: {
+ summary: 'GitHub issue/pull request detail',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'issueKind',
+ example: 'issues',
+ schema: { type: 'string', enum: this.getEnum('issueKind') },
+ },
+ {
+ name: 'property',
+ example: 'state',
+ schema: { type: 'string', enum: this.getEnum('property') },
+ },
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'number',
+ example: '979',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'issue/pull request',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'issue/pull request',
+ color: 'informational',
}
static render({ property, value, isPR, number }) {
@@ -213,7 +235,7 @@ module.exports = class GithubIssueDetail extends GithubAuthV3Service {
return this._requestJson({
url: `/repos/${user}/${repo}/${issueKind}/${number}`,
schema: propertyMap[property].schema,
- errorMessages: errorMessagesFor('issue, pull request or repo not found'),
+ httpErrors: httpErrorsFor('issue, pull request or repo not found'),
})
}
diff --git a/services/github/github-issue-detail.spec.js b/services/github/github-issue-detail.spec.js
index 54ab92f12d46f..f1cf1e0b8f737 100644
--- a/services/github/github-issue-detail.spec.js
+++ b/services/github/github-issue-detail.spec.js
@@ -1,14 +1,12 @@
-'use strict'
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { age, formatDate } from '../date.js'
+import { metric } from '../text-formatters.js'
+import { InvalidResponse } from '../index.js'
+import GithubIssueDetail from './github-issue-detail.service.js'
+import { issueStateColor, commentsColor } from './github-helpers.js'
-const { expect } = require('chai')
-const { test, given } = require('sazerac')
-const { age } = require('../color-formatters')
-const { formatDate, metric } = require('../text-formatters')
-const { InvalidResponse } = require('..')
-const GithubIssueDetail = require('./github-issue-detail.service')
-const { stateColor, commentsColor } = require('./github-helpers')
-
-describe('GithubIssueDetail', function() {
+describe('GithubIssueDetail', function () {
test(GithubIssueDetail.render, () => {
given({
property: 'state',
@@ -18,7 +16,7 @@ describe('GithubIssueDetail', function() {
}).expect({
label: 'pull request 12',
message: 'open',
- color: stateColor('open'),
+ color: issueStateColor('open'),
})
given({
property: 'state',
@@ -28,7 +26,27 @@ describe('GithubIssueDetail', function() {
}).expect({
label: 'issue 15',
message: 'closed',
- color: stateColor('closed'),
+ color: issueStateColor('closed'),
+ })
+ given({
+ property: 'state',
+ value: { state: 'not planned' },
+ number: '93',
+ isPR: false,
+ }).expect({
+ label: 'issue 93',
+ message: 'not planned',
+ color: issueStateColor('not planned'),
+ })
+ given({
+ property: 'state',
+ value: { state: 'duplicate' },
+ number: '95',
+ isPR: false,
+ }).expect({
+ label: 'issue 95',
+ message: 'duplicate',
+ color: issueStateColor('duplicate'),
})
given({
property: 'title',
@@ -92,6 +110,14 @@ describe('GithubIssueDetail', function() {
message: formatDate('2019-04-02T20:09:31Z'),
color: age('2019-04-02T20:09:31Z'),
})
+ given({
+ property: 'milestone',
+ value: 'MS 1',
+ }).expect({
+ label: 'milestone',
+ message: 'MS 1',
+ color: 'informational',
+ })
})
test(GithubIssueDetail.prototype.transform, () => {
@@ -100,9 +126,47 @@ describe('GithubIssueDetail', function() {
json: { state: 'closed' },
}).expect({
// Since it's a PR, the "merged" value is not crucial here.
- value: { state: 'closed', merged: true },
+ value: { state: 'closed', merged: false },
+ isPR: false,
+ })
+ given({
+ property: 'state',
+ json: { state: 'closed', state_reason: 'not_planned' },
+ }).expect({
+ value: { state: 'not planned', merged: false },
+ isPR: false,
+ })
+ given({
+ property: 'state',
+ json: { state: 'closed', state_reason: 'duplicate' },
+ }).expect({
+ value: { state: 'duplicate', merged: false },
+ isPR: false,
+ })
+ given({
+ property: 'state',
+ json: { state: 'closed', state_reason: 'other_reason' },
+ }).expect({
+ value: { state: 'closed', merged: false },
isPR: false,
})
+ given({
+ property: 'state',
+ json: { state: 'closed', pull_request: { merged_at: null } },
+ }).expect({
+ value: { state: 'closed', merged: false },
+ isPR: true,
+ })
+ given({
+ property: 'state',
+ json: {
+ state: 'closed',
+ pull_request: { merged_at: '2025-01-01T00:00:00Z' },
+ },
+ }).expect({
+ value: { state: 'closed', merged: true },
+ isPR: true,
+ })
given({
property: 'state',
issueKind: 'pulls',
@@ -180,10 +244,17 @@ describe('GithubIssueDetail', function() {
value: '2019-04-02T20:09:31Z',
isPR: false,
})
+ given({
+ property: 'milestone',
+ json: { milestone: { title: 'MS 1' } },
+ }).expect({
+ value: 'MS 1',
+ isPR: false,
+ })
})
- context('transform()', function() {
- it('throws InvalidResponse error when issue has no labels', function() {
+ context('transform()', function () {
+ it('throws InvalidResponse error when issue has no labels', function () {
try {
GithubIssueDetail.prototype.transform({
property: 'label',
@@ -196,4 +267,19 @@ describe('GithubIssueDetail', function() {
}
})
})
+
+ context('transform()', function () {
+ it('throws InvalidResponse error when issue has no milestone', function () {
+ try {
+ GithubIssueDetail.prototype.transform({
+ property: 'milestone',
+ json: { milestone: null },
+ })
+ expect.fail('Expected to throw')
+ } catch (e) {
+ expect(e).to.be.an.instanceof(InvalidResponse)
+ expect(e.prettyMessage).to.equal('no milestone')
+ }
+ })
+ })
})
diff --git a/services/github/github-issue-detail.tester.js b/services/github/github-issue-detail.tester.js
index 8b5775d928c0d..c13d0ec289470 100644
--- a/services/github/github-issue-detail.tester.js
+++ b/services/github/github-issue-detail.tester.js
@@ -1,14 +1,13 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isFormattedDate } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('github issue state')
.get('/issues/detail/state/badges/shields/979.json')
.expectBadge({
label: 'issue 979',
- message: Joi.equal('open', 'closed'),
+ message: 'closed',
})
t.create('github issue state (repo not found)')
@@ -35,7 +34,7 @@ t.create('github issue label')
label: 'label',
message: Joi.equal(
'bug | developer-experience',
- 'developer-experience | bug'
+ 'developer-experience | bug',
),
})
@@ -65,3 +64,16 @@ t.create('github pull request merge state (pull request not found)')
label: 'issue/pull request',
message: 'issue, pull request or repo not found',
})
+
+t.create('github issue milestone')
+ .get('/issues/detail/milestone/badges/shields/745.json')
+ .expectBadge({
+ label: 'milestone',
+ message: 'Next Deploy',
+ })
+
+t.create('github issue milestone (without milestone)')
+ .get('/issues/detail/milestone/badges/shields/979.json')
+ .expectBadge({
+ message: 'no milestone',
+ })
diff --git a/services/github/github-issues-search.service.js b/services/github/github-issues-search.service.js
new file mode 100644
index 0000000000000..298cb152bf639
--- /dev/null
+++ b/services/github/github-issues-search.service.js
@@ -0,0 +1,116 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
+
+const issuesSearchDocs = `
+For a full list of available filters and allowed values,
+see GitHub's documentation on
+[Searching issues and pull requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests)
+`
+
+const issueCountSchema = Joi.object({
+ data: Joi.object({
+ search: Joi.object({
+ issueCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ query: Joi.string().required(),
+}).required()
+
+class BaseGithubIssuesSearch extends GithubAuthV4Service {
+ static category = 'issue-tracking'
+ static defaultBadgeData = { label: 'query', color: 'informational' }
+
+ static render({ issueCount }) {
+ return { message: metric(issueCount) }
+ }
+
+ async fetch({ query }) {
+ const data = await this._requestGraphql({
+ query: gql`
+ query ($query: String!) {
+ search(query: $query, type: ISSUE) {
+ issueCount
+ }
+ }
+ `,
+ variables: { query },
+ schema: issueCountSchema,
+ transformErrors,
+ })
+ return data.data.search.issueCount
+ }
+}
+
+class GithubIssuesSearch extends BaseGithubIssuesSearch {
+ static route = {
+ base: 'github',
+ pattern: 'issues-search',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/issues-search': {
+ get: {
+ summary: 'GitHub issue custom search',
+ description: documentation,
+ parameters: [
+ queryParam({
+ name: 'query',
+ description: issuesSearchDocs,
+ example:
+ 'repo:badges/shields is:closed label:bug author:app/sentry-io',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ async handle(namedParams, { query }) {
+ const issueCount = await this.fetch({ query })
+ return this.constructor.render({ issueCount })
+ }
+}
+
+class GithubRepoIssuesSearch extends BaseGithubIssuesSearch {
+ static route = {
+ base: 'github',
+ pattern: 'issues-search/:user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/issues-search/{user}/{repo}': {
+ get: {
+ summary: 'GitHub issue custom search in repo',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ queryParam({
+ name: 'query',
+ description: issuesSearchDocs,
+ example: 'is:closed label:bug author:app/sentry-io',
+ required: true,
+ }),
+ ],
+ },
+ },
+ }
+
+ async handle({ user, repo }, { query }) {
+ query = `repo:${user}/${repo} ${query}`
+ const issueCount = await this.fetch({ query })
+ return this.constructor.render({ issueCount })
+ }
+}
+
+export { GithubIssuesSearch, GithubRepoIssuesSearch }
diff --git a/services/github/github-issues-search.tester.js b/services/github/github-issues-search.tester.js
new file mode 100644
index 0000000000000..d83acfdf7a55a
--- /dev/null
+++ b/services/github/github-issues-search.tester.js
@@ -0,0 +1,48 @@
+import { isMetric } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
+ id: 'GithubIssuesSearch',
+ title: 'Github Issues Search',
+ pathPrefix: '/github',
+})
+
+t.create('GitHub issue search (valid query string)')
+ .get(
+ '/issues-search.json?query=repo%3Abadges%2Fshields%20is%3Aclosed%20label%3Ablocker%20',
+ )
+ .expectBadge({
+ label: 'query',
+ message: isMetric,
+ })
+
+t.create('GitHub issue search (invalid query string)')
+ .get('/issues-search.json?query=')
+ .expectBadge({
+ label: 'query',
+ message: 'invalid query parameter: query',
+ })
+
+t.create('GitHub Repo issue search (valid query string)')
+ .get(
+ '/issues-search/badges/shields.json?query=is%3Aclosed%20label%3Ablocker%20',
+ )
+ .expectBadge({
+ label: 'query',
+ message: isMetric,
+ })
+
+t.create('GitHub Repo issue search (invalid query string)')
+ .get('/issues-search/badges/shields.json?query=')
+ .expectBadge({
+ label: 'query',
+ message: 'invalid query parameter: query',
+ })
+
+t.create('GitHub Repo issue search (invalid repo)')
+ .get(
+ '/issues-search/badges/helmets.json?query=is%3Aclosed%20label%3Ablocker%20',
+ )
+ .expectBadge({
+ label: 'query',
+ message: '0',
+ })
diff --git a/services/github/github-issues.service.js b/services/github/github-issues.service.js
index 52e580dfa1bd9..848c002b79705 100644
--- a/services/github/github-issues.service.js
+++ b/services/github/github-issues.service.js
@@ -1,11 +1,10 @@
-'use strict'
-
-const gql = require('graphql-tag')
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV4Service } = require('./github-auth-service')
-const { documentation, transformErrors } = require('./github-helpers')
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
const issueCountSchema = Joi.object({
data: Joi.object({
@@ -17,230 +16,56 @@ const issueCountSchema = Joi.object({
}).required(),
}).required()
-const pullRequestCountSchema = Joi.object({
- data: Joi.object({
- repository: Joi.object({
- pullRequests: Joi.object({
- totalCount: nonNegativeInteger,
- }).required(),
- }).required(),
- }).required(),
-}).required()
-
-const isPRVariant = {
- 'issues-pr': true,
- 'issues-pr-closed': true,
-}
-
const isClosedVariant = {
'issues-closed': true,
- 'issues-pr-closed': true,
+ 'issues-closed-raw': true,
}
-module.exports = class GithubIssues extends GithubAuthV4Service {
- static get category() {
- return 'issue-tracking'
+export default class GithubIssues extends GithubAuthV4Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github',
+ pattern:
+ ':variant(issues|issues-raw|issues-closed|issues-closed-raw)/:user/:repo/:label*',
}
- static get route() {
- return {
- base: 'github',
- pattern:
- ':variant(issues|issues-closed|issues-pr|issues-pr-closed):raw(-raw)?/:user/:repo/:label*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub issues',
- pattern: 'issues/:user/:repo',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: {
- label: 'issues',
- message: '167 open',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub issues',
- pattern: 'issues-raw/:user/:repo',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: {
- label: 'open issues',
- message: '167',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub issues by-label',
- pattern: 'issues/:user/:repo/:label',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- label: 'service-badge',
- },
- staticPreview: {
- label: 'service-badge issues',
- message: '110 open',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub issues by-label',
- pattern: 'issues-raw/:user/:repo/:label',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- label: 'service-badge',
- },
- staticPreview: {
- label: 'open service-badge issues',
- message: '110',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub closed issues',
- pattern: 'issues-closed/:user/:repo',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: {
- label: 'issues',
- message: '899 closed',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub closed issues',
- pattern: 'issues-closed-raw/:user/:repo',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: {
- label: 'closed issues',
- message: '899',
- color: 'yellow',
- },
- documentation,
- },
- {
- title: 'GitHub pull requests',
- pattern: 'issues-pr/:user/:repo',
- namedParams: {
- user: 'cdnjs',
- repo: 'cdnjs',
- },
- staticPreview: {
- label: 'pull requests',
- message: '136 open',
- color: 'yellow',
- },
- keywords: ['pullrequest', 'pr'],
- documentation,
- },
- {
- title: 'GitHub pull requests',
- pattern: 'issues-pr-raw/:user/:repo',
- namedParams: {
- user: 'cdnjs',
- repo: 'cdnjs',
- },
- staticPreview: {
- label: 'open pull requests',
- message: '136',
- color: 'yellow',
- },
- keywords: ['pullrequest', 'pr'],
- documentation,
- },
- {
- title: 'GitHub closed pull requests',
- pattern: 'issues-pr-closed/:user/:repo',
- namedParams: {
- user: 'cdnjs',
- repo: 'cdnjs',
- },
- staticPreview: {
- label: 'pull requests',
- message: '7k closed',
- color: 'yellow',
- },
- keywords: ['pullrequest', 'pr'],
- documentation,
- },
- {
- title: 'GitHub closed pull requests',
- pattern: 'issues-pr-closed-raw/:user/:repo',
- namedParams: {
- user: 'cdnjs',
- repo: 'cdnjs',
- },
- staticPreview: {
- label: 'closed pull requests',
- message: '7k',
- color: 'yellow',
- },
- keywords: ['pullrequest', 'pr'],
- documentation,
- },
- {
- title: 'GitHub pull requests by-label',
- pattern: 'issues-pr/:user/:repo/:label',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- label: 'service-badge',
- },
- staticPreview: {
- label: 'service-badge pull requests',
- message: '8 open',
- color: 'yellow',
- },
- keywords: ['pullrequests', 'pr'],
- documentation,
+ static openApi = {
+ '/github/{variant}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Issues',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'issues',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ ),
},
- {
- title: 'GitHub pull requests by-label',
- pattern: 'issues-pr-raw/:user/:repo/:label',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- label: 'service-badge',
- },
- staticPreview: {
- label: 'open service-badge pull requests',
- message: '8',
- color: 'yellow',
- },
- keywords: ['pullrequests', 'pr'],
- documentation,
+ },
+ '/github/{variant}/{user}/{repo}/{label}': {
+ get: {
+ summary: 'GitHub Issues by label',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'issues',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ { name: 'label', example: 'service-badge' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'issues',
- color: 'informational',
- }
- }
+ static defaultBadgeData = { label: 'issues', color: 'informational' }
- static render({ isPR, isClosed, issueCount, raw, label }) {
+ static render({ isClosed, issueCount, raw, label }) {
const state = isClosed ? 'closed' : 'open'
let labelPrefix = ''
@@ -255,96 +80,59 @@ module.exports = class GithubIssues extends GithubAuthV4Service {
const labelText = label
? `${isGhLabelMultiWord ? `"${label}"` : label} `
: ''
- const labelSuffix = isPR ? 'pull requests' : 'issues'
-
return {
- label: `${labelPrefix}${labelText}${labelSuffix}`,
- message: `${metric(issueCount)} ${messageSuffix}`,
+ label: `${labelPrefix}${labelText}issues`,
+ message: `${metric(issueCount)}${
+ messageSuffix ? ' ' : ''
+ }${messageSuffix}`,
color: issueCount > 0 ? 'yellow' : 'brightgreen',
}
}
- async fetch({ isPR, isClosed, user, repo, label }) {
- const commonVariables = {
- user,
- repo,
- labels: label ? [label] : undefined,
- }
- if (isPR) {
- const {
- data: {
- repository: {
- pullRequests: { totalCount },
- },
- },
- } = await this._requestGraphql({
- query: gql`
- query(
- $user: String!
- $repo: String!
- $states: [PullRequestState!]
- $labels: [String!]
- ) {
- repository(owner: $user, name: $repo) {
- pullRequests(states: $states, labels: $labels) {
- totalCount
- }
- }
- }
- `,
- variables: {
- ...commonVariables,
- states: isClosed ? ['MERGED', 'CLOSED'] : ['OPEN'],
+ async fetch({ isClosed, user, repo, label }) {
+ const {
+ data: {
+ repository: {
+ issues: { totalCount },
},
- schema: pullRequestCountSchema,
- transformErrors,
- })
- return { issueCount: totalCount }
- } else {
- const {
- data: {
- repository: {
- issues: { totalCount },
- },
- },
- } = await this._requestGraphql({
- query: gql`
- query(
- $user: String!
- $repo: String!
- $states: [IssueState!]
- $labels: [String!]
- ) {
- repository(owner: $user, name: $repo) {
- issues(states: $states, labels: $labels) {
- totalCount
- }
+ },
+ } = await this._requestGraphql({
+ query: gql`
+ query (
+ $user: String!
+ $repo: String!
+ $states: [IssueState!]
+ $labels: [String!]
+ ) {
+ repository(owner: $user, name: $repo) {
+ issues(states: $states, labels: $labels) {
+ totalCount
}
}
- `,
- variables: {
- ...commonVariables,
- states: isClosed ? ['CLOSED'] : ['OPEN'],
- },
- schema: issueCountSchema,
- transformErrors,
- })
- return { issueCount: totalCount }
- }
+ }
+ `,
+ variables: {
+ user,
+ repo,
+ labels: label ? [label] : undefined,
+ states: isClosed ? ['CLOSED'] : ['OPEN'],
+ },
+ schema: issueCountSchema,
+ transformErrors,
+ })
+ return { issueCount: totalCount }
}
- async handle({ variant, raw, user, repo, label }) {
- const isPR = isPRVariant[variant]
+ async handle({ variant, user, repo, label }) {
+ const raw = variant.endsWith('-raw')
const isClosed = isClosedVariant[variant]
const { issueCount } = await this.fetch({
- isPR,
isClosed,
user,
repo,
label,
})
return this.constructor.render({
- isPR,
isClosed,
issueCount,
raw,
diff --git a/services/github/github-issues.tester.js b/services/github/github-issues.tester.js
index 5e88f6d124c01..7b00294d69d07 100644
--- a/services/github/github-issues.tester.js
+++ b/services/github/github-issues.tester.js
@@ -1,45 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isMetric, isMetricOpenIssues } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('GitHub closed pull requests')
- .get('/issues-pr-closed/badges/shields.json')
- .expectBadge({
- label: 'pull requests',
- message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) closed$/
- ),
- })
-
-t.create('GitHub closed pull requests raw')
- .get('/issues-pr-closed-raw/badges/shields.json')
- .expectBadge({
- label: 'closed pull requests',
- message: isMetric,
- })
-
-t.create('GitHub pull requests')
- .get('/issues-pr/badges/shields.json')
- .expectBadge({
- label: 'pull requests',
- message: isMetricOpenIssues,
- })
-
-t.create('GitHub pull requests raw')
- .get('/issues-pr-raw/badges/shields.json')
- .expectBadge({
- label: 'open pull requests',
- message: isMetric,
- })
+import Joi from 'joi'
+import { isMetric, isMetricOpenIssues } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('GitHub closed issues')
.get('/issues-closed/badges/shields.json')
.expectBadge({
label: 'issues',
message: Joi.string().regex(
- /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) closed$/
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) closed$/,
),
})
@@ -50,12 +19,10 @@ t.create('GitHub closed issues raw')
message: isMetric,
})
-t.create('GitHub open issues')
- .get('/issues/badges/shields.json')
- .expectBadge({
- label: 'issues',
- message: isMetricOpenIssues,
- })
+t.create('GitHub open issues').get('/issues/badges/shields.json').expectBadge({
+ label: 'issues',
+ message: isMetricOpenIssues,
+})
t.create('GitHub open issues raw')
.get('/issues-raw/badges/shields.json')
@@ -84,9 +51,9 @@ t.create('GitHub open issues by label (raw)')
// https://github.com/badges/shields/issues/1870
t.create('GitHub open issues by label including slash character (raw)')
- .get('/issues-raw/IgorNovozhilov/ndk/@ndk/cfg.json')
+ .get('/issues-raw/calebcartwright/shields-service-test-target/@foo/bar.json')
.expectBadge({
- label: 'open @ndk/cfg issues',
+ label: 'open @foo/bar issues',
// Not always > 0.
message: Joi.alternatives(isMetric, Joi.equal('0')),
})
@@ -97,17 +64,3 @@ t.create('GitHub open issues (repo not found)')
label: 'issues',
message: 'repo not found',
})
-
-t.create('GitHub open pull requests by label')
- .get('/issues-pr/badges/shields/service-badge.json')
- .expectBadge({
- label: 'service-badge pull requests',
- message: isMetricOpenIssues,
- })
-
-t.create('GitHub open pull requests by label (raw)')
- .get('/issues-pr-raw/badges/shields/service-badge.json')
- .expectBadge({
- label: 'open service-badge pull requests',
- message: isMetric,
- })
diff --git a/services/github/github-labels.service.js b/services/github/github-labels.service.js
index e12942f53f58e..85913f1f10d77 100644
--- a/services/github/github-labels.service.js
+++ b/services/github/github-labels.service.js
@@ -1,47 +1,39 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
- color: Joi.string()
- .hex()
- .required(),
+ color: Joi.string().hex().required(),
}).required()
-module.exports = class GithubLabels extends GithubAuthV3Service {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'github/labels',
- pattern: ':user/:repo/:name',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub labels',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- name: 'help-wanted',
- },
- staticPreview: this.render({ name: 'help-wanted', color: '#159818' }),
- documentation,
+export default class GithubLabels extends GithubAuthV3Service {
+ static category = 'issue-tracking'
+ static route = { base: 'github/labels', pattern: ':user/:repo/:name' }
+ static openApi = {
+ '/github/labels/{user}/{repo}/{name}': {
+ get: {
+ summary: 'GitHub labels',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'atom',
+ },
+ {
+ name: 'repo',
+ example: 'atom',
+ },
+ {
+ name: 'name',
+ example: 'help-wanted',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: ' ',
- }
- }
+ static defaultBadgeData = { label: ' ' }
static render({ name, color }) {
return {
@@ -54,7 +46,7 @@ module.exports = class GithubLabels extends GithubAuthV3Service {
return this._requestJson({
url: `/repos/${user}/${repo}/labels/${name}`,
schema,
- errorMessages: errorMessagesFor(`repo or label not found`),
+ httpErrors: httpErrorsFor('repo or label not found'),
})
}
diff --git a/services/github/github-labels.tester.js b/services/github/github-labels.tester.js
index 1fdde71604af7..d493e03ffd850 100644
--- a/services/github/github-labels.tester.js
+++ b/services/github/github-labels.tester.js
@@ -1,13 +1,10 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('labels')
- .get('/badges/shields/bug.json')
- .expectBadge({
- message: 'bug',
- color: '#e11d21',
- })
+t.create('labels').get('/badges/shields/bug.json').expectBadge({
+ message: 'bug',
+ color: '#e11d21',
+})
t.create('labels (repo or label not found)')
.get('/badges/shields/somenonexistentlabelthatwouldneverexist.json')
diff --git a/services/github/github-language-count.service.js b/services/github/github-language-count.service.js
index c7f84dfbca58c..29f2e57990c55 100644
--- a/services/github/github-language-count.service.js
+++ b/services/github/github-language-count.service.js
@@ -1,43 +1,35 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseGithubLanguage } from './github-languages-base.js'
+import { documentation } from './github-helpers.js'
-const { BaseGithubLanguage } = require('./github-languages-base')
-const { documentation } = require('./github-helpers')
-
-module.exports = class GithubLanguageCount extends BaseGithubLanguage {
- static get category() {
- return 'analysis'
- }
-
- static get route() {
- return {
- base: 'github/languages/count',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub language count',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: this.render({ count: 5 }),
- documentation,
+export default class GithubLanguageCount extends BaseGithubLanguage {
+ static category = 'analysis'
+ static route = { base: 'github/languages/count', pattern: ':user/:repo' }
+ static openApi = {
+ '/github/languages/count/{user}/{repo}': {
+ get: {
+ summary: 'GitHub language count',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'languages',
- }
- }
+ static defaultBadgeData = { label: 'languages' }
static render({ count }) {
return {
- message: count,
+ message: metric(count),
color: 'blue',
}
}
diff --git a/services/github/github-language-count.tester.js b/services/github/github-language-count.tester.js
index 6de2a888f5bcf..b7464402995f4 100644
--- a/services/github/github-language-count.tester.js
+++ b/services/github/github-language-count.tester.js
@@ -1,16 +1,11 @@
-'use strict'
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('language count')
- .get('/badges/shields.json')
- .expectBadge({
- label: 'languages',
- message: Joi.number()
- .integer()
- .positive(),
- })
+t.create('language count').get('/badges/shields.json').expectBadge({
+ label: 'languages',
+ message: Joi.number().integer().positive(),
+})
t.create('language count (empty repo)')
.get('/pyvesb/emptyrepo.json')
diff --git a/services/github/github-languages-base.js b/services/github/github-languages-base.js
index 61c9b4470c05d..667f12a47e7bb 100644
--- a/services/github/github-languages-base.js
+++ b/services/github/github-languages-base.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { httpErrorsFor } from './github-helpers.js'
/*
We're expecting a response like { "Python": 39624, "Shell": 104 }
@@ -16,7 +14,7 @@ class BaseGithubLanguage extends GithubAuthV3Service {
return this._requestJson({
url: `/repos/${user}/${repo}/languages`,
schema,
- errorMessages: errorMessagesFor(),
+ httpErrors: httpErrorsFor(),
})
}
@@ -25,4 +23,4 @@ class BaseGithubLanguage extends GithubAuthV3Service {
}
}
-module.exports = { BaseGithubLanguage }
+export { BaseGithubLanguage }
diff --git a/services/github/github-last-commit.service.js b/services/github/github-last-commit.service.js
index 499b928a904af..bc90ac914b741 100644
--- a/services/github/github-last-commit.service.js
+++ b/services/github/github-last-commit.service.js
@@ -1,14 +1,9 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { formatDate } = require('../text-formatters')
-const { age: ageColor } = require('../color-formatters')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
-const commonExampleAttrs = {
- keywords: ['latest'],
- documentation,
-}
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { relativeUri } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.array()
.items(
@@ -17,73 +12,97 @@ const schema = Joi.array()
author: Joi.object({
date: Joi.string().required(),
}).required(),
+ committer: Joi.object({
+ date: Joi.string().required(),
+ }).required(),
}).required(),
- }).required()
+ }),
)
.required()
-module.exports = class GithubLastCommit extends GithubAuthV3Service {
- static get category() {
- return 'activity'
- }
+const displayEnum = ['author', 'committer']
+
+const queryParamSchema = Joi.object({
+ path: relativeUri,
+ display_timestamp: Joi.string()
+ .valid(...displayEnum)
+ .default('author'),
+}).required()
- static get route() {
- return {
- base: 'github/last-commit',
- pattern: ':user/:repo/:branch*',
- }
+export default class GithubLastCommit extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = {
+ base: 'github/last-commit',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub last commit',
- pattern: ':user/:repo',
- namedParams: {
- user: 'google',
- repo: 'skia',
- },
- staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
- ...commonExampleAttrs,
+ static openApi = {
+ '/github/last-commit/{user}/{repo}': {
+ get: {
+ summary: 'GitHub last commit',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'google' }),
+ pathParam({ name: 'repo', example: 'skia' }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ queryParam({
+ name: 'display_timestamp',
+ example: 'committer',
+ schema: { type: 'string', enum: displayEnum },
+ description: 'Defaults to `author` if not specified',
+ }),
+ ],
},
- {
- title: 'GitHub last commit (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'google',
- repo: 'skia',
- branch: 'infra/config',
- },
- staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
- ...commonExampleAttrs,
+ },
+ '/github/last-commit/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub last commit (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'google' }),
+ pathParam({ name: 'repo', example: 'skia' }),
+ pathParam({ name: 'branch', example: 'infra/config' }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ queryParam({
+ name: 'display_timestamp',
+ example: 'committer',
+ schema: { type: 'string', enum: displayEnum },
+ description: 'Defaults to `author` if not specified',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'last commit',
- }
- }
-
- static render({ commitDate }) {
- return {
- message: formatDate(commitDate),
- color: ageColor(Date.parse(commitDate)),
- }
- }
+ static defaultBadgeData = { label: 'last commit' }
- async fetch({ user, repo, branch }) {
+ async fetch({ user, repo, branch, path }) {
return this._requestJson({
url: `/repos/${user}/${repo}/commits`,
- options: { qs: { sha: branch } },
+ options: { searchParams: { sha: branch, path, per_page: 1 } },
schema,
- errorMessages: errorMessagesFor(),
+ httpErrors: httpErrorsFor(),
})
}
- async handle({ user, repo, branch }) {
- const body = await this.fetch({ user, repo, branch })
- return this.constructor.render({ commitDate: body[0].commit.author.date })
+ async handle({ user, repo, branch }, queryParams) {
+ const { path, display_timestamp: displayTimestamp } = queryParams
+ const body = await this.fetch({ user, repo, branch, path })
+ const [commit] = body.map(obj => obj.commit)
+
+ if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
+
+ return renderDateBadge(commit[displayTimestamp].date)
}
}
diff --git a/services/github/github-last-commit.tester.js b/services/github/github-last-commit.tester.js
index 5ee66e832de3e..fe82cc7425178 100644
--- a/services/github/github-last-commit.tester.js
+++ b/services/github/github-last-commit.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isFormattedDate } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('last commit (recent)')
.get('/eslint/eslint.json')
@@ -15,6 +14,34 @@ t.create('last commit (on branch)')
.get('/badges/badgr.co/shielded.json')
.expectBadge({ label: 'last commit', message: 'july 2013' })
+t.create('last commit (by top-level file path)')
+ .get('/badges/badgr.co.json?path=README.md')
+ .expectBadge({ label: 'last commit', message: 'september 2013' })
+
+t.create('last commit (by top-level dir path)')
+ .get('/badges/badgr.co.json?path=badgr')
+ .expectBadge({ label: 'last commit', message: 'june 2013' })
+
+t.create('last commit (by top-level dir path with trailing slash)')
+ .get('/badges/badgr.co.json?path=badgr/')
+ .expectBadge({ label: 'last commit', message: 'june 2013' })
+
+t.create('last commit (by nested file path)')
+ .get('/badges/badgr.co.json?path=badgr/colors.py')
+ .expectBadge({ label: 'last commit', message: 'june 2013' })
+
+t.create('last commit (on branch) (by top-level file path)')
+ .get('/badges/badgr.co/shielded.json?path=README.md')
+ .expectBadge({ label: 'last commit', message: 'june 2013' })
+
+t.create('last commit (by committer)')
+ .get('/badges/badgr.co/shielded.json?display_timestamp=committer')
+ .expectBadge({ label: 'last commit', message: 'july 2013' })
+
t.create('last commit (repo not found)')
.get('/badges/helmets.json')
.expectBadge({ label: 'last commit', message: 'repo not found' })
+
+t.create('last commit (no commits found)')
+ .get('/badges/badgr.co/shielded.json?path=not/a/dir')
+ .expectBadge({ label: 'last commit', message: 'no commits found' })
diff --git a/services/github/github-lerna-json.service.js b/services/github/github-lerna-json.service.js
index 58acf942ae864..b523c1b34120b 100644
--- a/services/github/github-lerna-json.service.js
+++ b/services/github/github-lerna-json.service.js
@@ -1,55 +1,62 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { semver } = require('../validators')
-const { ConditionalGithubAuthV3Service } = require('./github-auth-service')
-const { fetchJsonFromRepo } = require('./github-common-fetch')
-const { documentation } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { semver } from '../validators.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchJsonFromRepo } from './github-common-fetch.js'
+import { documentation } from './github-helpers.js'
const versionSchema = Joi.object({
version: Joi.alternatives().try(semver, Joi.equal('independent').required()),
}).required()
-module.exports = class GithubLernaJson extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'github/lerna-json/v',
- pattern: ':user/:repo/:branch*',
- }
+export default class GithubLernaJson extends ConditionalGithubAuthV3Service {
+ static category = 'version'
+ static route = {
+ base: 'github/lerna-json/v',
+ pattern: ':user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'Github lerna version',
- pattern: ':user/:repo',
- namedParams: { user: 'babel', repo: 'babel' },
- staticPreview: this.render({ version: '7.6.4' }),
- documentation,
+ static openApi = {
+ '/github/lerna-json/v/{user}/{repo}': {
+ get: {
+ summary: 'GitHub lerna version',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'babel',
+ },
+ {
+ name: 'repo',
+ example: 'babel',
+ },
+ ),
},
- {
- title: 'Github lerna version (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: { user: 'jneander', repo: 'jneander', branch: 'colors' },
- staticPreview: this.render({
- version: 'independent',
- branch: 'colors',
- }),
- documentation,
+ },
+ '/github/lerna-json/v/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub lerna version (branch)',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'jneander',
+ },
+ {
+ name: 'repo',
+ example: 'jneander',
+ },
+ {
+ name: 'branch',
+ example: 'colors',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'lerna',
- }
- }
+ static defaultBadgeData = { label: 'lerna' }
static render({ version, branch }) {
return renderVersionBadge({
diff --git a/services/github/github-lerna-json.tester.js b/services/github/github-lerna-json.tester.js
index 82119de29f5ab..95694ee27d72a 100644
--- a/services/github/github-lerna-json.tester.js
+++ b/services/github/github-lerna-json.tester.js
@@ -1,28 +1,21 @@
-'use strict'
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('Lerna version').get('/facebook/jest.json').expectBadge({
+ label: 'lerna',
+ message: isSemver,
+})
-t.create('Lerna version')
- .get('/babel/babel.json')
- .expectBadge({
- label: 'lerna',
- message: isSemver,
- })
+t.create('Lerna version (independent)').get('/imba/imba.json').expectBadge({
+ label: 'lerna',
+ message: 'independent',
+})
-t.create('Lerna version (independent)')
- .get('/jneander/jneander.json')
- .expectBadge({
- label: 'lerna',
- message: 'independent',
- })
-
-t.create('Lerna version (branch)')
- .get('/babel/babel/master.json')
- .expectBadge({
- label: 'lerna@master',
- message: isSemver,
- })
+t.create('Lerna version (branch)').get('/facebook/jest/main.json').expectBadge({
+ label: 'lerna@main',
+ message: isSemver,
+})
t.create('Lerna version (lerna.json missing)')
.get('/PyvesB/empty-repo.json')
diff --git a/services/github/github-license.service.js b/services/github/github-license.service.js
index 21787c206eab8..c1410e7196ebe 100644
--- a/services/github/github-license.service.js
+++ b/services/github/github-license.service.js
@@ -1,47 +1,37 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
// Some repos do not have a license, in which case GitHub returns `{ license: null }`.
license: Joi.object({ spdx_id: Joi.string().required() }).allow(null),
}).required()
-module.exports = class GithubLicense extends GithubAuthV3Service {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return {
- base: 'github/license',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub',
- namedParams: { user: 'mashape', repo: 'apistatus' },
- staticPreview: {
- label: 'license',
- message: 'MIT',
- color: 'green',
- },
- documentation,
+export default class GithubLicense extends GithubAuthV3Service {
+ static category = 'license'
+ static route = { base: 'github/license', pattern: ':user/:repo' }
+ static openApi = {
+ '/github/license/{user}/{repo}': {
+ get: {
+ summary: 'GitHub License',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'mashape',
+ },
+ {
+ name: 'repo',
+ example: 'apistatus',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'license',
- }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ license }) {
if (license === 'NOASSERTION') {
@@ -57,7 +47,7 @@ module.exports = class GithubLicense extends GithubAuthV3Service {
const { license: licenseObject } = await this._requestJson({
schema,
url: `/repos/${user}/${repo}`,
- errorMessages: errorMessagesFor('repo not found'),
+ httpErrors: httpErrorsFor('repo not found'),
})
const license = licenseObject ? licenseObject.spdx_id : undefined
diff --git a/services/github/github-license.spec.js b/services/github/github-license.spec.js
index 82359204cc8db..87167fc26d516 100644
--- a/services/github/github-license.spec.js
+++ b/services/github/github-license.spec.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const GithubLicense = require('./github-license.service')
+import { test, given } from 'sazerac'
+import GithubLicense from './github-license.service.js'
test(GithubLicense.render, () => {
given({ license: undefined }).expect({ message: 'not specified' })
diff --git a/services/github/github-license.tester.js b/services/github/github-license.tester.js
index 261b4b4126616..cb36d7b3dbb5e 100644
--- a/services/github/github-license.tester.js
+++ b/services/github/github-license.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { licenseToColor } = require('../licenses')
-const t = (module.exports = require('../tester').createServiceTester())
+import { licenseToColor } from '../licenses.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const publicDomainLicenseColor = licenseToColor('CC0-1.0')
const unknownLicenseColor = licenseToColor()
@@ -45,7 +44,7 @@ t.create('License with SPDX id not appearing in configuration')
url: 'https://api.github.com/licenses/efl-1.0',
featured: true,
},
- })
+ }),
)
.expectBadge({
label: 'license',
diff --git a/services/github/github-manifest.service.js b/services/github/github-manifest.service.js
index 53de9b0c45412..4d88b49f2f3a6 100644
--- a/services/github/github-manifest.service.js
+++ b/services/github/github-manifest.service.js
@@ -1,58 +1,57 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const {
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import {
individualValueSchema,
transformAndValidate,
renderDynamicBadge,
-} = require('../dynamic-common')
-const { ConditionalGithubAuthV3Service } = require('./github-auth-service')
-const { fetchJsonFromRepo } = require('./github-common-fetch')
-const { documentation } = require('./github-helpers')
+} from '../dynamic-common.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchJsonFromRepo } from './github-common-fetch.js'
+import { documentation } from './github-helpers.js'
const schema = Joi.object({
version: individualValueSchema,
}).required()
+const queryParamSchema = Joi.object({
+ filename: Joi.string().regex(/.*manifest\.json$/),
+})
+
const flexibleSchema = Joi.object().required()
class GithubManifestVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'github/manifest-json/v',
- pattern: ':user/:repo/:branch*',
- }
+ static category = 'version'
+ static route = {
+ base: 'github/manifest-json/v',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub manifest version',
- pattern: ':user/:repo',
- namedParams: {
- user: 'RedSparr0w',
- repo: 'IndieGala-Helper',
- },
- staticPreview: this.render({ version: 2 }),
- documentation,
+ static openApi = {
+ '/github/manifest-json/v/{user}/{repo}': {
+ get: {
+ summary: 'GitHub manifest version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'sindresorhus' }),
+ pathParam({ name: 'repo', example: 'show-all-github-issues' }),
+ queryParam({ name: 'filename', example: 'extension/manifest.json' }),
+ ],
},
- {
- title: 'GitHub manifest version',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'RedSparr0w',
- repo: 'IndieGala-Helper',
- branch: 'master',
- },
- staticPreview: this.render({ version: 2, branch: 'master' }),
- documentation,
+ },
+ '/github/manifest-json/v/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub manifest version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'sindresorhus' }),
+ pathParam({ name: 'repo', example: 'show-all-github-issues' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({ name: 'filename', example: 'extension/manifest.json' }),
+ ],
},
- ]
+ },
}
static render({ version, branch }) {
@@ -63,70 +62,55 @@ class GithubManifestVersion extends ConditionalGithubAuthV3Service {
})
}
- async handle({ user, repo, branch }) {
+ async handle({ user, repo, branch }, { filename = 'manifest.json' }) {
const { version } = await fetchJsonFromRepo(this, {
schema,
user,
repo,
branch,
- filename: 'manifest.json',
+ filename,
})
return this.constructor.render({ version, branch })
}
}
class DynamicGithubManifest extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'other'
+ static category = 'other'
+ static route = {
+ base: 'github/manifest-json',
+ pattern: ':key([^v/][^/]*)/:user/:repo/:branch*',
+ queryParamSchema,
}
- static get route() {
- return {
- base: 'github/manifest-json',
- pattern: ':key([^v/][^/]*)/:user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub manifest.json dynamic',
- pattern: ':key/:user/:repo',
- namedParams: {
- key: 'permissions',
- user: 'developit',
- repo: 'microbundle',
- },
- staticPreview: this.render({
- key: 'permissions',
- value: ['bundle', 'rollup', 'micro library'],
- }),
- documentation,
+ static openApi = {
+ '/github/manifest-json/{key}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub manifest.json dynamic',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'key', example: 'permissions' }),
+ pathParam({ name: 'user', example: 'sindresorhus' }),
+ pathParam({ name: 'repo', example: 'show-all-github-issues' }),
+ queryParam({ name: 'filename', example: 'extension/manifest.json' }),
+ ],
},
- {
- title: 'GitHub manifest.json dynamic',
- pattern: ':key/:user/:repo/:branch',
- namedParams: {
- key: 'permissions',
- user: 'developit',
- repo: 'microbundle',
- branch: 'master',
- },
- staticPreview: this.render({
- key: 'permissions',
- value: ['bundle', 'rollup', 'micro library'],
- branch: 'master',
- }),
- documentation,
+ },
+ '/github/manifest-json/{key}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub manifest.json dynamic (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'key', example: 'permissions' }),
+ pathParam({ name: 'user', example: 'sindresorhus' }),
+ pathParam({ name: 'repo', example: 'show-all-github-issues' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ queryParam({ name: 'filename', example: 'extension/manifest.json' }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'manifest',
- }
- }
+ static defaultBadgeData = { label: 'manifest' }
static render({ key, value, branch }) {
return renderDynamicBadge({
@@ -136,7 +120,7 @@ class DynamicGithubManifest extends ConditionalGithubAuthV3Service {
})
}
- async handle({ key, user, repo, branch }) {
+ async handle({ key, user, repo, branch }, { filename = 'manifest.json' }) {
// Not sure `manifest-json/n` was ever advertised, but it was supported.
if (key === 'n') {
key = 'name'
@@ -146,14 +130,11 @@ class DynamicGithubManifest extends ConditionalGithubAuthV3Service {
user,
repo,
branch,
- filename: 'manifest.json',
+ filename,
})
const value = transformAndValidate({ data, key })
return this.constructor.render({ key, value, branch })
}
}
-module.exports = {
- GithubManifestVersion,
- DynamicGithubManifest,
-}
+export { GithubManifestVersion, DynamicGithubManifest }
diff --git a/services/github/github-manifest.tester.js b/services/github/github-manifest.tester.js
index 3811c77f0ed4f..827bbe7acf4d2 100644
--- a/services/github/github-manifest.tester.js
+++ b/services/github/github-manifest.tester.js
@@ -1,35 +1,54 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubManifest',
title: 'GithubManifest',
pathPrefix: '/github/manifest-json',
-}))
+})
t.create('Manifest version')
- .get('/v/RedSparr0w/IndieGala-Helper.json')
+ .get('/v/sindresorhus/show-all-github-issues.json')
.expectBadge({
- label: 'version',
+ label: 'manifest',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
+t.create('Manifest version (path)')
+ .get('/v/RedSparr0w/IndieGala-Helper.json?filename=extension/manifest.json')
+ .expectBadge({
+ label: 'manifest',
message: isVPlusDottedVersionAtLeastOne,
})
-t.create('Manifest name')
- .get('/n/RedSparr0w/IndieGala-Helper.json')
+t.create('Manifest version (path not found)')
+ .get(
+ '/v/RedSparr0w/IndieGala-Helper.json?filename=invalid-directory/manifest.json',
+ )
+ .expectBadge({
+ label: 'version',
+ message:
+ 'repo not found, branch not found, or invalid-directory/manifest.json missing',
+ })
+
+t.create('Manifest name (path)')
+ .get('/n/RedSparr0w/IndieGala-Helper.json?filename=extension/manifest.json')
.expectBadge({ label: 'name', message: 'IndieGala Helper' })
-t.create('Manifest array')
- .get('/permissions/RedSparr0w/IndieGala-Helper.json')
+t.create('Manifest array (path)')
+ .get(
+ '/permissions/RedSparr0w/IndieGala-Helper.json?filename=extension/manifest.json',
+ )
.expectBadge({
label: 'permissions',
message: Joi.string().regex(/.*?,/),
})
-t.create('Manifest object')
- .get('/background/RedSparr0w/IndieGala-Helper.json')
+t.create('Manifest object (path)')
+ .get(
+ '/background/RedSparr0w/IndieGala-Helper.json?filename=extension/manifest.json',
+ )
.expectBadge({ label: 'manifest', message: 'invalid key value' })
t.create('Manifest invalid json response')
diff --git a/services/github/github-milestone-detail.service.js b/services/github/github-milestone-detail.service.js
index 1cbbf6cd894e8..c0c6b1f92494b 100644
--- a/services/github/github-milestone-detail.service.js
+++ b/services/github/github-milestone-detail.service.js
@@ -1,10 +1,9 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
open_issues: nonNegativeInteger,
@@ -12,47 +11,45 @@ const schema = Joi.object({
title: Joi.string().required(),
}).required()
-module.exports = class GithubMilestoneDetail extends GithubAuthV3Service {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'github/milestones',
- pattern:
- ':variant(issues-closed|issues-open|issues-total|progress|progress-percent)/:user/:repo/:number([0-9]+)',
- }
+export default class GithubMilestoneDetail extends GithubAuthV3Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github/milestones',
+ pattern:
+ ':variant(issues-closed|issues-open|issues-total|progress|progress-percent)/:user/:repo/:number([0-9]+)',
}
- static get examples() {
- return [
- {
- title: 'GitHub milestone',
- namedParams: {
- variant: 'issues-open',
- user: 'badges',
- repo: 'shields',
- number: '1',
- },
- staticPreview: {
- label: 'milestone issues',
- message: '17/22',
- color: 'blue',
- },
- documentation,
+ static openApi = {
+ '/github/milestones/{variant}/{user}/{repo}/{number}': {
+ get: {
+ summary: 'GitHub milestone details',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'issues-open',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'number',
+ example: '1',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'milestones',
- color: 'informational',
- }
- }
+ static defaultBadgeData = { label: 'milestones', color: 'informational' }
- static render({ user, repo, variant, number, milestone }) {
+ static render({ variant, milestone }) {
let milestoneMetric
let color
let label = ''
@@ -74,24 +71,24 @@ module.exports = class GithubMilestoneDetail extends GithubAuthV3Service {
label = 'issues'
break
case 'progress':
- milestoneMetric = `${milestone.closed_issues}/${milestone.open_issues +
- milestone.closed_issues}`
+ milestoneMetric = `${milestone.closed_issues}/${
+ milestone.open_issues + milestone.closed_issues
+ }`
color = 'blue'
break
case 'progress-percent':
milestoneMetric = `${Math.floor(
(milestone.closed_issues /
(milestone.open_issues + milestone.closed_issues)) *
- 100
+ 100,
)}%`
color = 'blue'
}
return {
- label: `${milestone.title} ${label}`,
+ label: `${milestone.title}${label ? ' ' : ''}${label}`,
message: metric(milestoneMetric),
color,
- link: [`https://github.com/${user}/${repo}/milestone/${number}`],
}
}
@@ -99,12 +96,12 @@ module.exports = class GithubMilestoneDetail extends GithubAuthV3Service {
return this._requestJson({
url: `/repos/${user}/${repo}/milestones/${number}`,
schema,
- errorMessages: errorMessagesFor(`repo or milestone not found`),
+ httpErrors: httpErrorsFor('repo or milestone not found'),
})
}
async handle({ user, repo, variant, number }) {
const milestone = await this.fetch({ user, repo, number })
- return this.constructor.render({ user, repo, variant, number, milestone })
+ return this.constructor.render({ variant, milestone })
}
}
diff --git a/services/github/github-milestone-detail.tester.js b/services/github/github-milestone-detail.tester.js
index 699c5afac464d..4305a6074b19d 100644
--- a/services/github/github-milestone-detail.tester.js
+++ b/services/github/github-milestone-detail.tester.js
@@ -1,18 +1,16 @@
-'use strict'
-
-const {
+import {
isMetric,
isMetricOverMetric,
isIntegerPercentage,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Milestone Open Issues')
.get('/issues-open/MacroPower/milestone-test/1.json')
.expectBadge({
label: 'openWithOneOpenIssue open issues',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestone/1`],
})
t.create('Milestone Closed Issues')
@@ -20,7 +18,6 @@ t.create('Milestone Closed Issues')
.expectBadge({
label: 'closedWithOneClosedIssue closed issues',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestone/3`],
})
t.create('Milestone Total Issues')
@@ -28,7 +25,6 @@ t.create('Milestone Total Issues')
.expectBadge({
label: 'openWithOneOpenOneClosedIssue issues',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestone/2`],
})
t.create('Milestone Progress')
@@ -36,7 +32,6 @@ t.create('Milestone Progress')
.expectBadge({
label: 'openWithOneOpenOneClosedIssue',
message: isMetricOverMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestone/2`],
})
t.create('Milestone Progress (Percent)')
@@ -44,7 +39,6 @@ t.create('Milestone Progress (Percent)')
.expectBadge({
label: 'openWithOneOpenOneClosedIssue',
message: isIntegerPercentage,
- link: [`https://github.com/MacroPower/milestone-test/milestone/2`],
})
t.create('Milestones (repo or milestone not found)')
diff --git a/services/github/github-milestone.service.js b/services/github/github-milestone.service.js
index 9103f73790ce8..f7874ecbd1d9d 100644
--- a/services/github/github-milestone.service.js
+++ b/services/github/github-milestone.service.js
@@ -1,60 +1,57 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.array()
.items(
Joi.object({
state: Joi.string().required(),
- })
+ }),
)
.required()
-module.exports = class GithubMilestone extends GithubAuthV3Service {
- static get category() {
- return 'issue-tracking'
- }
-
- static get route() {
- return {
- base: 'github/milestones',
- pattern: ':variant(open|closed|all)/:user/:repo',
- }
+export default class GithubMilestone extends GithubAuthV3Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github/milestones',
+ pattern: ':variant(open|closed|all)/:user/:repo',
}
- static get examples() {
- return [
- {
- title: 'GitHub milestones',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- variant: 'open',
- },
- staticPreview: {
- label: 'milestones',
- message: '2',
- color: 'red',
- },
- documentation,
+ static openApi = {
+ '/github/milestones/{variant}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub number of milestones',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'open',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'milestones',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'milestones',
+ color: 'informational',
}
- static render({ user, repo, variant, milestones }) {
+ static render({ variant, milestones }) {
const milestoneLength = milestones.length
let color
- let label = ''
+ let qualifier = ''
switch (variant) {
case 'all':
@@ -62,19 +59,18 @@ module.exports = class GithubMilestone extends GithubAuthV3Service {
break
case 'open':
color = 'red'
- label = 'active'
+ qualifier = 'active'
break
case 'closed':
color = 'green'
- label = 'completed'
+ qualifier = 'completed'
break
}
return {
- label: `${label} milestones`,
+ label: `${qualifier}${qualifier ? ' ' : ''}milestones`,
message: metric(milestoneLength),
color,
- link: [`https://github.com/${user}/${repo}/milestones`],
}
}
@@ -82,12 +78,12 @@ module.exports = class GithubMilestone extends GithubAuthV3Service {
return this._requestJson({
url: `/repos/${user}/${repo}/milestones?state=${variant}`,
schema,
- errorMessages: errorMessagesFor(`repo not found`),
+ httpErrors: httpErrorsFor('repo not found'),
})
}
async handle({ user, repo, variant }) {
const milestones = await this.fetch({ user, repo, variant })
- return this.constructor.render({ user, repo, variant, milestones })
+ return this.constructor.render({ variant, milestones })
}
}
diff --git a/services/github/github-milestone.tester.js b/services/github/github-milestone.tester.js
index d219ea91db542..dd9b92234ad72 100644
--- a/services/github/github-milestone.tester.js
+++ b/services/github/github-milestone.tester.js
@@ -1,14 +1,12 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('All Milestones')
.get('/all/MacroPower/milestone-test.json')
.expectBadge({
label: 'milestones',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestones`],
})
t.create('Open Milestones')
@@ -16,7 +14,6 @@ t.create('Open Milestones')
.expectBadge({
label: 'active milestones',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestones`],
})
t.create('Closed Milestones')
@@ -24,7 +21,6 @@ t.create('Closed Milestones')
.expectBadge({
label: 'completed milestones',
message: isMetric,
- link: [`https://github.com/MacroPower/milestone-test/milestones`],
})
t.create('Milestones (repo not found)')
diff --git a/services/github/github-package-json.service.js b/services/github/github-package-json.service.js
index 36dab5e031be4..f5236b29a0e7d 100644
--- a/services/github/github-package-json.service.js
+++ b/services/github/github-package-json.service.js
@@ -1,61 +1,56 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const {
- transformAndValidate,
- renderDynamicBadge,
-} = require('../dynamic-common')
-const {
+import Joi from 'joi'
+import { pathParam, pathParams, queryParam } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { transformAndValidate, renderDynamicBadge } from '../dynamic-common.js'
+import {
isPackageJsonWithDependencies,
getDependencyVersion,
-} = require('../package-json-helpers')
-const { semver } = require('../validators')
-const { ConditionalGithubAuthV3Service } = require('./github-auth-service')
-const { fetchJsonFromRepo } = require('./github-common-fetch')
-const { documentation } = require('./github-helpers')
-
-const keywords = ['npm', 'node']
+} from '../package-json-helpers.js'
+import { semver } from '../validators.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchJsonFromRepo } from './github-common-fetch.js'
+import { documentation } from './github-helpers.js'
const versionSchema = Joi.object({
version: semver,
}).required()
-class GithubPackageJsonVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'version'
- }
+const subfolderQueryParamSchema = Joi.object({
+ filename: Joi.string(),
+}).required()
- static get route() {
- return {
- base: 'github/package-json/v',
- pattern: ':user/:repo/:branch*',
- }
+class GithubPackageJsonVersion extends ConditionalGithubAuthV3Service {
+ static category = 'version'
+ static route = {
+ base: 'github/package-json/v',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema: subfolderQueryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub package.json version',
- pattern: ':user/:repo',
- namedParams: { user: 'IcedFrisby', repo: 'IcedFrisby' },
- staticPreview: this.render({ version: '2.0.0-alpha.2' }),
- documentation,
- keywords,
+ static openApi = {
+ '/github/package-json/v/{user}/{repo}': {
+ get: {
+ summary: 'GitHub package.json version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ queryParam({ name: 'filename', example: 'badge-maker/package.json' }),
+ ],
},
- {
- title: 'GitHub package.json version (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'IcedFrisby',
- repo: 'IcedFrisby',
- branch: 'master',
- },
- staticPreview: this.render({ version: '2.0.0-alpha.2' }),
- documentation,
- keywords,
+ },
+ '/github/package-json/v/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub package.json version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({ name: 'filename', example: 'badge-maker/package.json' }),
+ ],
},
- ]
+ },
}
static render({ version, branch }) {
@@ -66,96 +61,126 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthV3Service {
})
}
- async handle({ user, repo, branch }) {
+ async handle({ user, repo, branch }, { filename = 'package.json' }) {
const { version } = await fetchJsonFromRepo(this, {
schema: versionSchema,
user,
repo,
branch,
- filename: 'package.json',
+ filename,
})
return this.constructor.render({ version, branch })
}
}
-const dependencyQueryParamSchema = Joi.object({
- filename: Joi.string(),
-}).required()
+const packageNameDescription =
+ 'This may be the name of an unscoped package like `package-name` or a [scoped package](https://docs.npmjs.com/about-scopes) like `@author/package-name`'
class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'platform-support'
+ static category = 'platform-support'
+ static route = {
+ base: 'github/package-json/dependency-version',
+ pattern:
+ ':user/:repo/:kind(dev|peer|optional)?/:scope(@[^/]+)?/:packageName/:branch*',
+ queryParamSchema: subfolderQueryParamSchema,
}
- static get route() {
- return {
- base: 'github/package-json/dependency-version',
- pattern:
- ':user/:repo/:kind(dev|peer)?/:scope(@[^/]+)?/:packageName/:branch*',
- queryParamSchema: dependencyQueryParamSchema,
- }
- }
-
- static get examples() {
- return [
+ static openApi = {
+ '/github/package-json/dependency-version/{user}/{repo}/{packageName}': {
+ get: {
+ summary: 'GitHub package.json prod dependency version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({
+ name: 'packageName',
+ example: 'dayjs',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'filename',
+ example: 'badge-maker/package.json',
+ }),
+ ],
+ },
+ },
+ '/github/package-json/dependency-version/{user}/{repo}/{packageName}/{branch}':
{
- title: 'GitHub package.json dependency version (prod)',
- pattern: ':user/:repo/:packageName',
- namedParams: {
- user: 'developit',
- repo: 'microbundle',
- packageName: 'rollup',
+ get: {
+ summary: 'GitHub package.json prod dependency version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'badges' }),
+ pathParam({ name: 'repo', example: 'shields' }),
+ pathParam({
+ name: 'packageName',
+ example: 'dayjs',
+ description: packageNameDescription,
+ }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'filename',
+ example: 'badge-maker/package.json',
+ }),
+ ],
},
- staticPreview: this.render({
- dependency: 'rollup',
- range: '^0.67.3',
- }),
- documentation,
- keywords,
},
+ '/github/package-json/dependency-version/{user}/{repo}/{kind}/{packageName}':
{
- title: 'GitHub package.json dependency version (dev dep on branch)',
- pattern: ':user/:repo/dev/:scope?/:packageName/:branch*',
- namedParams: {
- user: 'zeit',
- repo: 'next.js',
- branch: 'canary',
- scope: '@babel',
- packageName: 'preset-react',
+ get: {
+ summary: 'GitHub package.json dev/peer/optional dependency version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'gatsbyjs' }),
+ pathParam({ name: 'repo', example: 'gatsby' }),
+ pathParam({
+ name: 'kind',
+ example: 'dev',
+ schema: { type: 'string', enum: this.getEnum('kind') },
+ }),
+ pathParam({
+ name: 'packageName',
+ example: 'cross-env',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'filename',
+ example: 'packages/gatsby-cli/package.json',
+ }),
+ ],
},
- staticPreview: this.render({
- dependency: '@babel/preset-react',
- range: '7.0.0',
- }),
- documentation,
- keywords,
},
+ '/github/package-json/dependency-version/{user}/{repo}/{kind}/{packageName}/{branch}':
{
- title: 'GitHub package.json dependency version (subfolder of monorepo)',
- pattern: ':user/:repo/:packageName',
- namedParams: {
- user: 'metabolize',
- repo: 'anafanafo',
- packageName: 'puppeteer',
- },
- queryParams: {
- filename: 'packages/char-width-table-builder/package.json',
+ get: {
+ summary:
+ 'GitHub package.json dev/peer/optional dependency version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'gatsbyjs' }),
+ pathParam({ name: 'repo', example: 'gatsby' }),
+ pathParam({
+ name: 'kind',
+ example: 'dev',
+ schema: { type: 'string', enum: this.getEnum('kind') },
+ }),
+ pathParam({
+ name: 'packageName',
+ example: 'cross-env',
+ description: packageNameDescription,
+ }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'filename',
+ example: 'packages/gatsby-cli/package.json',
+ }),
+ ],
},
- staticPreview: this.render({
- dependency: 'puppeteer',
- range: '^1.14.0',
- }),
- documentation,
- keywords,
},
- ]
}
- static get defaultBadgeData() {
- return {
- label: 'dependency',
- }
- }
+ static defaultBadgeData = { label: 'dependency' }
static render({ dependency, range }) {
return {
@@ -166,13 +191,14 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
}
async handle(
- { user, repo, kind, branch = 'master', scope, packageName },
- { filename = 'package.json' }
+ { user, repo, kind, branch = 'HEAD', scope, packageName },
+ { filename = 'package.json' },
) {
const {
dependencies,
devDependencies,
peerDependencies,
+ optionalDependencies,
} = await fetchJsonFromRepo(this, {
schema: isPackageJsonWithDependencies,
user,
@@ -182,12 +208,13 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
})
const wantedDependency = scope ? `${scope}/${packageName}` : packageName
- const { range } = getDependencyVersion({
+ const range = getDependencyVersion({
kind,
wantedDependency,
dependencies,
devDependencies,
peerDependencies,
+ optionalDependencies,
})
return this.constructor.render({
@@ -200,59 +227,47 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
// This must be exported after GithubPackageJsonVersion in order for the
// former to work correctly.
class DynamicGithubPackageJson extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'github/package-json',
- pattern: ':key/:user/:repo/:branch*',
- }
+ static category = 'other'
+ static route = {
+ base: 'github/package-json',
+ pattern: ':key/:user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'GitHub package.json dynamic',
- pattern: ':key/:user/:repo',
- namedParams: {
- key: 'keywords',
- user: 'developit',
- repo: 'microbundle',
- },
- staticPreview: this.render({
- key: 'keywords',
- value: ['bundle', 'rollup', 'micro library'],
- }),
- documentation,
- keywords,
+ static openApi = {
+ '/github/package-json/{key}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub package.json dynamic',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'key',
+ example: 'keywords',
+ description: 'any key in package.json',
+ },
+ { name: 'user', example: 'developit' },
+ { name: 'repo', example: 'microbundle' },
+ ),
},
- {
- title: 'GitHub package.json dynamic',
- pattern: ':key/:user/:repo/:branch',
- namedParams: {
- key: 'keywords',
- user: 'developit',
- repo: 'microbundle',
- branch: 'master',
- },
- staticPreview: this.render({
- key: 'keywords',
- value: ['bundle', 'rollup', 'micro library'],
- branch: 'master',
- }),
- documentation,
- keywords,
+ },
+ '/github/package-json/{key}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub package.json dynamic (branch)',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'key',
+ example: 'keywords',
+ description: 'any key in package.json',
+ },
+ { name: 'user', example: 'developit' },
+ { name: 'repo', example: 'microbundle' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'package.json',
- }
- }
+ static defaultBadgeData = { label: 'package.json' }
static render({ key, value, branch }) {
return renderDynamicBadge({
@@ -274,12 +289,16 @@ class DynamicGithubPackageJson extends ConditionalGithubAuthV3Service {
branch,
filename: 'package.json',
})
- const value = transformAndValidate({ data, key })
+ let value = transformAndValidate({ data, key })
+ // Strip build metadata suffix from packageManager field (e.g. yarn@3.2.3+sha224.abc -> yarn@3.2.3)
+ if (key === 'packageManager' && typeof value === 'string') {
+ value = value.replace(/\+.*$/, '')
+ }
return this.constructor.render({ key, value, branch })
}
}
-module.exports = [
+export default [
GithubPackageJsonVersion,
GithubPackageJsonDependencyVersion,
DynamicGithubPackageJson,
diff --git a/services/github/github-package-json.tester.js b/services/github/github-package-json.tester.js
index 9349b85924831..4a3c90bd8103a 100644
--- a/services/github/github-package-json.tester.js
+++ b/services/github/github-package-json.tester.js
@@ -1,22 +1,18 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import { isSemver } from '../test-validators.js'
+import { semverRange } from '../validators.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { isSemver } = require('../test-validators')
-const { semverRange } = require('../validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubPackageJson',
title: 'GithubPackageJson',
pathPrefix: '/github/package-json',
-}))
+})
-t.create('Package version')
- .get('/v/badges/shields.json')
- .expectBadge({
- label: 'version',
- message: isSemver,
- })
+t.create('Package version').get('/v/badges/shields.json').expectBadge({
+ label: 'version',
+ message: isSemver,
+})
t.create('Package version (repo not found)')
.get('/v/badges/helmets.json')
@@ -25,6 +21,17 @@ t.create('Package version (repo not found)')
message: 'repo not found, branch not found, or package.json missing',
})
+t.create('Package version (monorepo)')
+ .get(
+ `/v/metabolize/anafanafo.json?filename=${encodeURIComponent(
+ 'packages/char-width-table-builder/package.json',
+ )}`,
+ )
+ .expectBadge({
+ label: 'version',
+ message: isSemver,
+ })
+
t.create('Package name')
.get('/n/badges/shields.json')
.expectBadge({ label: 'name', message: 'shields.io' })
@@ -51,9 +58,16 @@ t.create('Peer dependency version')
message: semverRange,
})
+t.create('Optional dependency version')
+ .get('/dependency-version/IcedFrisby/IcedFrisby/optional/@hapi/joi.json')
+ .expectBadge({
+ label: '@hapi/joi',
+ message: semverRange,
+ })
+
t.create('Dev dependency version')
.get(
- '/dependency-version/paulmelnikow/react-boxplot/dev/react.json?label=react%20tested'
+ '/dependency-version/paulmelnikow/react-boxplot/dev/react.json?label=react%20tested',
)
.expectBadge({
label: 'react tested',
@@ -70,8 +84,8 @@ t.create('Prod dependency version')
t.create('Prod dependency version (monorepo)')
.get(
`/dependency-version/metabolize/anafanafo/puppeteer.json?filename=${encodeURIComponent(
- 'packages/char-width-table-builder/package.json'
- )}`
+ 'packages/char-width-table-builder/package.json',
+ )}`,
)
.expectBadge({
label: 'puppeteer',
@@ -79,16 +93,16 @@ t.create('Prod dependency version (monorepo)')
})
t.create('Scoped dependency')
- .get('/dependency-version/badges/shields/dev/@babel/core.json')
+ .get('/dependency-version/badges/shields/dev/@docusaurus/core.json')
.expectBadge({
- label: '@babel/core',
+ label: '@docusaurus/core',
message: semverRange,
})
t.create('Scoped dependency on branch')
- .get('/dependency-version/zeit/next.js/dev/babel-eslint/alpha.json')
+ .get('/dependency-version/zeit/next.js/dev/@babel/eslint-parser/canary.json')
.expectBadge({
- label: 'babel-eslint',
+ label: '@babel/eslint-parser',
message: semverRange,
})
@@ -98,3 +112,17 @@ t.create('Unknown dependency')
label: 'dependency',
message: 'dev dependency not found',
})
+
+t.create('Package manager (strips build metadata suffix)')
+ .get('/packageManager/nodejs/corepack.json')
+ .expectBadge({
+ label: 'packageManager',
+ message: Joi.string().regex(/^(npm|yarn|pnpm|bun)@[\d.]+$/),
+ })
+
+t.create('Package manager (repo not found)')
+ .get('/packageManager/badges/helmets.json')
+ .expectBadge({
+ label: 'package.json',
+ message: 'repo not found, branch not found, or package.json missing',
+ })
diff --git a/services/github/github-pipenv.service.js b/services/github/github-pipenv.service.js
index ab291e6aa1149..eb5be7253603b 100644
--- a/services/github/github-pipenv.service.js
+++ b/services/github/github-pipenv.service.js
@@ -1,98 +1,75 @@
-'use strict'
-
-const { renderVersionBadge } = require('../version')
-const { isLockfile, getDependencyVersion } = require('../pipenv-helpers')
-const { addv } = require('../text-formatters')
-const { NotFound } = require('..')
-const { ConditionalGithubAuthV3Service } = require('./github-auth-service')
-const { fetchJsonFromRepo } = require('./github-common-fetch')
-const { documentation: githubDocumentation } = require('./github-helpers')
-
-const keywords = ['pipfile']
-
-const documentation = `
-
- Pipenv is a dependency
- manager for Python which manages a
- virtualenv for
- projects. It adds/removes packages from your Pipfile as
- you install/uninstall packages and generates the ever-important
- Pipfile.lock, which can be checked in to source control
- in order to produce deterministic builds.
-
-
-
- The GitHub Pipenv badges are intended for applications using Pipenv
- which are hosted on GitHub.
-
-
-
- When Pipfile.lock is checked in, the GitHub Pipenv
- locked dependency version badge displays the locked version of
- a dependency listed in [packages] or
- [dev-packages] (or any of their transitive dependencies).
-
-
-
- Usually a Python version is specified in the Pipfile, which
- pipenv lock then places in Pipfile.lock. The
- GitHub Pipenv Python version badge displays that version.
-
+import { pep440VersionColor } from '../color-formatters.js'
+import { renderVersionBadge } from '../version.js'
+import { isLockfile, getDependencyVersion } from '../pipenv-helpers.js'
+import { addv } from '../text-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchJsonFromRepo } from './github-common-fetch.js'
+import { documentation as githubDocumentation } from './github-helpers.js'
+
+const description = `
+[Pipenv](https://github.com/pypa/pipenv) is a dependency
+manager for Python which manages a
+[virtualenv](https://virtualenv.pypa.io/en/latest/) for
+projects. It adds/removes packages from your \`Pipfile\` as
+you install/uninstall packages and generates the ever-important
+\`Pipfile.lock\`, which can be checked in to source control
+in order to produce deterministic builds.
+
+The GitHub Pipenv badges are intended for applications using Pipenv
+which are hosted on GitHub.
+
+When \`Pipfile.lock\` is checked in, the GitHub Pipenv
+locked dependency version badge displays the locked version of
+a dependency listed in \`[packages]\` or
+\`[dev-packages]\` (or any of their transitive dependencies).
+
+Usually a Python version is specified in the \`Pipfile\`, which
+\`pipenv lock\` then places in \`Pipfile.lock\`.
+The GitHub Pipenv Python version badge displays that version.
${githubDocumentation}
`
class GithubPipenvLockedPythonVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'platform-support'
+ static category = 'platform-support'
+ static route = {
+ base: 'github/pipenv/locked/python-version',
+ pattern: ':user/:repo/:branch*',
}
- static get route() {
- return {
- base: 'github/pipenv/locked/python-version',
- pattern: ':user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub Pipenv locked Python version',
- pattern: ':user/:repo',
- namedParams: {
- user: 'metabolize',
- repo: 'rq-dashboard-on-heroku',
- },
- staticPreview: this.render({ version: '3.7' }),
- documentation,
- keywords,
+ static openApi = {
+ '/github/pipenv/locked/python-version/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Pipenv locked Python version',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ ),
},
- {
- title: 'GitHub Pipenv locked Python version (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'metabolize',
- repo: 'rq-dashboard-on-heroku',
- branch: 'master',
- },
- staticPreview: this.render({ version: '3.7', branch: 'master' }),
- documentation,
- keywords,
+ },
+ '/github/pipenv/locked/python-version/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub Pipenv locked Python version (branch)',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ { name: 'branch', example: 'main' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'python',
- }
- }
+ static defaultBadgeData = { label: 'python' }
static render({ version, branch }) {
return renderVersionBadge({
version,
tag: branch,
defaultLabel: 'python',
+ versionFormatter: pep440VersionColor,
})
}
@@ -116,62 +93,71 @@ class GithubPipenvLockedPythonVersion extends ConditionalGithubAuthV3Service {
}
class GithubPipenvLockedDependencyVersion extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'dependencies'
+ static category = 'dependencies'
+ static route = {
+ base: 'github/pipenv/locked/dependency-version',
+ pattern: ':user/:repo/:kind(dev)?/:packageName/:branch*',
}
- static get route() {
- return {
- base: 'github/pipenv/locked/dependency-version',
- pattern: ':user/:repo/:kind(dev)?/:packageName/:branch*',
- }
- }
-
- static get examples() {
- return [
+ static openApi = {
+ '/github/pipenv/locked/dependency-version/{user}/{repo}/{packageName}': {
+ get: {
+ summary: 'GitHub Pipenv locked dependency version',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ { name: 'packageName', example: 'flask' },
+ ),
+ },
+ },
+ '/github/pipenv/locked/dependency-version/{user}/{repo}/{packageName}/{branch}':
{
- title: 'GitHub Pipenv locked dependency version',
- pattern: ':user/:repo/:kind(dev)?/:packageName',
- namedParams: {
- user: 'metabolize',
- repo: 'rq-dashboard-on-heroku',
- packageName: 'flask',
+ get: {
+ summary: 'GitHub Pipenv locked dependency version (branch)',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ { name: 'packageName', example: 'flask' },
+ { name: 'branch', example: 'main' },
+ ),
},
- staticPreview: this.render({
- dependency: 'flask',
- version: '1.1.1',
- }),
- documentation,
- keywords: ['python', ...keywords],
},
+ '/github/pipenv/locked/dependency-version/{user}/{repo}/dev/{packageName}':
{
- title: 'GitHub Pipenv locked dependency version (branch)',
- pattern: ':user/:repo/:kind(dev)?/:packageName/:branch',
- namedParams: {
- user: 'metabolize',
- repo: 'rq-dashboard-on-heroku',
- kind: 'dev',
- packageName: 'black',
- branch: 'master',
+ get: {
+ summary: 'GitHub Pipenv locked dev dependency version',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ { name: 'packageName', example: 'black' },
+ ),
+ },
+ },
+ '/github/pipenv/locked/dependency-version/{user}/{repo}/dev/{packageName}/{branch}':
+ {
+ get: {
+ summary: 'GitHub Pipenv locked dev dependency version (branch)',
+ description,
+ parameters: pathParams(
+ { name: 'user', example: 'metabolize' },
+ { name: 'repo', example: 'rq-dashboard-on-heroku' },
+ { name: 'packageName', example: 'black' },
+ { name: 'branch', example: 'main' },
+ ),
},
- staticPreview: this.render({ dependency: 'black', version: '19.3b0' }),
- documentation,
- keywords: ['python', ...keywords],
},
- ]
}
- static get defaultBadgeData() {
- return {
- label: 'dependency',
- }
- }
+ static defaultBadgeData = { label: 'dependency' }
static render({ dependency, version, ref }) {
return {
label: dependency,
message: version ? addv(version) : ref,
- color: 'blue',
+ color: version ? pep440VersionColor(version) : 'blue',
}
}
@@ -196,7 +182,7 @@ class GithubPipenvLockedDependencyVersion extends ConditionalGithubAuthV3Service
}
}
-module.exports = [
+export default [
GithubPipenvLockedPythonVersion,
GithubPipenvLockedDependencyVersion,
]
diff --git a/services/github/github-pipenv.tester.js b/services/github/github-pipenv.tester.js
index 88019391a8ff5..bb291e8615aef 100644
--- a/services/github/github-pipenv.tester.js
+++ b/services/github/github-pipenv.tester.js
@@ -1,21 +1,19 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const {
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
+ isCommitHash,
isVPlusDottedVersionAtLeastOne,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
+} from '../test-validators.js'
// e.g. v19.3b0
const isBlackVersion = Joi.string().regex(/^v\d+(\.\d+)*(.*)?$/)
-const isShortSha = Joi.string().regex(/[0-9a-f]{7}/)
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubPipenv',
title: 'GithubPipenv',
pathPrefix: '/github/pipenv',
-}))
+})
t.create('Locked Python version')
.get('/locked/python-version/metabolize/rq-dashboard-on-heroku.json')
@@ -40,7 +38,7 @@ t.create('Locked Python version (pipfile.lock has no python version)')
t.create('Locked version of default dependency')
.get(
- '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/rq-dashboard.json'
+ '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/rq-dashboard.json',
)
.expectBadge({
label: 'rq-dashboard',
@@ -49,7 +47,7 @@ t.create('Locked version of default dependency')
t.create('Locked version of default dependency (branch)')
.get(
- '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/rq-dashboard/master.json'
+ '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/rq-dashboard/main.json',
)
.expectBadge({
label: 'rq-dashboard',
@@ -58,7 +56,7 @@ t.create('Locked version of default dependency (branch)')
t.create('Locked version of dev dependency')
.get(
- '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/black.json'
+ '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/black.json',
)
.expectBadge({
label: 'black',
@@ -67,7 +65,7 @@ t.create('Locked version of dev dependency')
t.create('Locked version of dev dependency (branch)')
.get(
- '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/black/master.json'
+ '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/black/main.json',
)
.expectBadge({
label: 'black',
@@ -76,7 +74,7 @@ t.create('Locked version of dev dependency (branch)')
t.create('Locked version of unknown dependency')
.get(
- '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/i-made-this-up.json'
+ '/locked/dependency-version/metabolize/rq-dashboard-on-heroku/dev/i-made-this-up.json',
)
.expectBadge({
label: 'dependency',
@@ -84,10 +82,8 @@ t.create('Locked version of unknown dependency')
})
t.create('Locked version of VCS dependency')
- .get(
- '/locked/dependency-version/DemocracyClub/aggregator-api/dc-base-theme.json'
- )
+ .get('/locked/dependency-version/ykdojo/editdojo/tweepy.json')
.expectBadge({
- label: 'dc-base-theme',
- message: isShortSha,
+ label: 'tweepy',
+ message: isCommitHash,
})
diff --git a/services/github/github-pull-request-check-state.service.js b/services/github/github-pull-request-check-state.service.js
index 81ab826fbe1cd..badf6fb236f1c 100644
--- a/services/github/github-pull-request-check-state.service.js
+++ b/services/github/github-pull-request-check-state.service.js
@@ -1,10 +1,23 @@
-'use strict'
+import Joi from 'joi'
+import countBy from 'lodash.countby'
+import { pathParams } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { fetchIssue } from './github-common-fetch.js'
+import {
+ documentation as commonDocumentation,
+ httpErrorsFor,
+} from './github-helpers.js'
-const Joi = require('@hapi/joi')
-const countBy = require('lodash.countby')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { fetchIssue } = require('./github-common-fetch')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+const description = `
+Displays the status of a pull request, as reported by the Commit Status API.
+
+Note: Nowadays, GitHub Actions and many third party integrations report state via
+the Checks API. If this badge does not show expected values, please try out one of our
+[Check Runs badges](https://shields.io/search/?q=GitHub+check+runs) instead. You can read more about status checks in
+the [GitHub documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks).
+
+${commonDocumentation}
+`
const schema = Joi.object({
state: Joi.equal('failure', 'pending', 'success').required(),
@@ -12,64 +25,62 @@ const schema = Joi.object({
.items(
Joi.object({
state: Joi.equal('error', 'failure', 'pending', 'success').required(),
- })
+ }),
)
.default([]),
}).required()
-const keywords = ['pullrequest', 'detail']
-
-module.exports = class GithubPullRequestCheckState extends GithubAuthV3Service {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'github/status',
- pattern: ':variant(s|contexts)/pulls/:user/:repo/:number(\\d+)',
- }
+export default class GithubPullRequestCheckState extends GithubAuthV3Service {
+ static category = 'build'
+ static route = {
+ base: 'github/status',
+ pattern: ':variant(s|contexts)/pulls/:user/:repo/:number(\\d+)',
}
- static get examples() {
- return [
- {
- title: 'GitHub pull request check state',
- pattern: 's/pulls/:user/:repo/:number',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- number: '1110',
- },
- staticPreview: this.render({ variant: 's', state: 'pending' }),
- keywords,
- documentation,
+ static openApi = {
+ '/github/status/s/pulls/{user}/{repo}/{number}': {
+ get: {
+ summary: 'GitHub pull request status',
+ description,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'number',
+ example: '1110',
+ },
+ ),
},
- {
- title: 'GitHub pull request check contexts',
- pattern: 'contexts/pulls/:user/:repo/:number',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- number: '1110',
- },
- staticPreview: this.render({
- variant: 'contexts',
- state: 'pending',
- stateCounts: { passed: 5, pending: 1 },
- }),
- keywords,
- documentation,
+ },
+ '/github/status/contexts/pulls/{user}/{repo}/{number}': {
+ get: {
+ summary: 'GitHub pull request check contexts',
+ description,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ {
+ name: 'number',
+ example: '1110',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'checks',
- namedLogo: 'github',
- }
- }
+ static defaultBadgeData = { label: 'checks' }
static render({ variant, state, stateCounts }) {
let message
@@ -106,7 +117,7 @@ module.exports = class GithubPullRequestCheckState extends GithubAuthV3Service {
const json = await this._requestJson({
schema,
url: `/repos/${user}/${repo}/commits/${ref}/status`,
- errorMessages: errorMessagesFor('commit not found'),
+ httpErrors: httpErrorsFor('commit not found'),
})
const { state, stateCounts } = this.constructor.transform(json)
diff --git a/services/github/github-pull-request-check-state.tester.js b/services/github/github-pull-request-check-state.tester.js
index 1505846992b80..d079d166b6971 100644
--- a/services/github/github-pull-request-check-state.tester.js
+++ b/services/github/github-pull-request-check-state.tester.js
@@ -1,24 +1,23 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('github pull request check state')
- .get('/s/pulls/badges/shields/1110.json')
- .expectBadge({ label: 'checks', message: 'failure' })
+ .get('/s/pulls/badges/shields/11053.json')
+ .expectBadge({ label: 'checks', message: 'success' })
t.create('github pull request check state (pull request not found)')
- .get('/s/pulls/badges/shields/5110.json')
+ .get('/s/pulls/badges/shields/5101.json')
.expectBadge({ label: 'checks', message: 'pull request or repo not found' })
t.create(
- "github pull request check state (ref returned by github doesn't exist"
+ "github pull request check state (ref returned by github doesn't exist)",
)
.get('/s/pulls/badges/shields/1110.json')
.intercept(
nock =>
nock('https://api.github.com', { allowUnmocked: true })
.get('/repos/badges/shields/pulls/1110')
- .reply(200, JSON.stringify({ head: { sha: 'abc123' } })) // Looks like a real ref, but isn't.
+ .reply(200, JSON.stringify({ head: { sha: 'abcde12356' } })), // Looks like a real ref, but isn't.
)
.networkOn()
.expectBadge({
@@ -27,5 +26,5 @@ t.create(
})
t.create('github pull request check contexts')
- .get('/contexts/pulls/badges/shields/1110.json')
- .expectBadge({ label: 'checks', message: '1 failure' })
+ .get('/contexts/pulls/badges/shields/11053.json')
+ .expectBadge({ label: 'checks', message: '1 success' })
diff --git a/services/github/github-pull-requests.service.js b/services/github/github-pull-requests.service.js
new file mode 100644
index 0000000000000..f42941d17dde2
--- /dev/null
+++ b/services/github/github-pull-requests.service.js
@@ -0,0 +1,226 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { pathParams, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
+
+const pullRequestCountSchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ pullRequests: Joi.object({
+ totalCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const pullRequestSearchSchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ id: Joi.string().required(),
+ }).required(),
+ search: Joi.object({
+ issueCount: nonNegativeInteger,
+ }).required(),
+ }).required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ excludeDrafts: Joi.equal(''),
+ onlyDrafts: Joi.equal(''),
+})
+ .oxor('excludeDrafts', 'onlyDrafts')
+ .required()
+
+const pullRequestVariants = [
+ 'issues-pr',
+ 'issues-pr-raw',
+ 'issues-pr-closed',
+ 'issues-pr-closed-raw',
+]
+
+const variantSummaries = {
+ 'issues-pr': 'GitHub Pull Requests',
+ 'issues-pr-raw': 'GitHub Pull Requests (raw)',
+ 'issues-pr-closed': 'GitHub Closed Pull Requests',
+ 'issues-pr-closed-raw': 'GitHub Closed Pull Requests (raw)',
+}
+
+function openApiRoute({ variant, label }) {
+ return {
+ get: {
+ summary: `${variantSummaries[variant]}${label ? ' by label' : ''}`,
+ description: documentation,
+ parameters: [
+ ...pathParams(
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ ...(label ? [{ name: 'label', example: 'service-badge' }] : []),
+ ),
+ queryParam({
+ name: 'excludeDrafts',
+ description: 'Exclude draft pull requests',
+ example: null,
+ schema: { type: 'boolean' },
+ }),
+ queryParam({
+ name: 'onlyDrafts',
+ description: 'Only include draft pull requests',
+ example: null,
+ schema: { type: 'boolean' },
+ }),
+ ],
+ },
+ }
+}
+
+const openApi = Object.fromEntries(
+ pullRequestVariants.flatMap(variant => [
+ [
+ `/github/${variant}/{user}/{repo}`,
+ openApiRoute({ variant, label: false }),
+ ],
+ [
+ `/github/${variant}/{user}/{repo}/{label}`,
+ openApiRoute({ variant, label: true }),
+ ],
+ ]),
+)
+
+export default class GithubPullRequests extends GithubAuthV4Service {
+ static category = 'issue-tracking'
+ static route = {
+ base: 'github',
+ pattern:
+ ':variant(issues-pr|issues-pr-raw|issues-pr-closed|issues-pr-closed-raw)/:user/:repo/:label*',
+ queryParamSchema,
+ }
+
+ static openApi = openApi
+
+ static defaultBadgeData = {
+ label: 'pull requests',
+ color: 'informational',
+ }
+
+ static render({
+ issueCount,
+ label,
+ raw,
+ isClosed,
+ excludeDrafts,
+ onlyDrafts,
+ }) {
+ const state = isClosed ? 'closed' : 'open'
+ const isGhLabelMultiWord = label && label.includes(' ')
+ const labelText = label
+ ? `${isGhLabelMultiWord ? `"${label}"` : label} `
+ : ''
+ let draftText = ''
+ if (excludeDrafts) {
+ draftText = 'non-draft '
+ } else if (onlyDrafts) {
+ draftText = 'draft '
+ }
+
+ return {
+ label: `${raw ? `${state} ` : ''}${labelText}${draftText}pull requests`,
+ message: `${metric(issueCount)}${raw ? '' : ` ${state}`}`,
+ color: issueCount > 0 ? 'yellow' : 'brightgreen',
+ }
+ }
+
+ async fetch({ user, repo, label, isClosed, excludeDrafts, onlyDrafts }) {
+ if (!excludeDrafts && !onlyDrafts) {
+ const {
+ data: {
+ repository: {
+ pullRequests: { totalCount },
+ },
+ },
+ } = await this._requestGraphql({
+ query: gql`
+ query (
+ $user: String!
+ $repo: String!
+ $states: [PullRequestState!]
+ $labels: [String!]
+ ) {
+ repository(owner: $user, name: $repo) {
+ pullRequests(states: $states, labels: $labels) {
+ totalCount
+ }
+ }
+ }
+ `,
+ variables: {
+ user,
+ repo,
+ states: isClosed ? ['MERGED', 'CLOSED'] : ['OPEN'],
+ labels: label ? [label] : undefined,
+ },
+ schema: pullRequestCountSchema,
+ transformErrors,
+ })
+ return totalCount
+ }
+
+ const searchQualifiers = [
+ `repo:${user}/${repo}`,
+ 'is:pr',
+ `is:${isClosed ? 'closed' : 'open'}`,
+ ]
+ if (label) {
+ searchQualifiers.push(`label:"${label}"`)
+ }
+ if (excludeDrafts) {
+ searchQualifiers.push('draft:false')
+ }
+ if (onlyDrafts) {
+ searchQualifiers.push('draft:true')
+ }
+
+ const data = await this._requestGraphql({
+ query: gql`
+ query ($query: String!, $user: String!, $repo: String!) {
+ repository(owner: $user, name: $repo) {
+ id
+ }
+ search(query: $query, type: ISSUE) {
+ issueCount
+ }
+ }
+ `,
+ variables: { query: searchQualifiers.join(' '), user, repo },
+ schema: pullRequestSearchSchema,
+ transformErrors,
+ })
+
+ return data.data.search.issueCount
+ }
+
+ async handle({ variant, user, repo, label }, queryParams) {
+ const raw = variant.endsWith('-raw')
+ const isClosed = variant.includes('-closed')
+ const excludeDrafts = queryParams.excludeDrafts !== undefined
+ const onlyDrafts = queryParams.onlyDrafts !== undefined
+ const issueCount = await this.fetch({
+ user,
+ repo,
+ label,
+ isClosed,
+ excludeDrafts,
+ onlyDrafts,
+ })
+ return this.constructor.render({
+ issueCount,
+ label,
+ raw,
+ isClosed,
+ excludeDrafts,
+ onlyDrafts,
+ })
+ }
+}
diff --git a/services/github/github-pull-requests.tester.js b/services/github/github-pull-requests.tester.js
new file mode 100644
index 0000000000000..8fba3f8a610c6
--- /dev/null
+++ b/services/github/github-pull-requests.tester.js
@@ -0,0 +1,102 @@
+import Joi from 'joi'
+import {
+ isMetric,
+ isMetricClosedIssues,
+ isMetricOpenIssues,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('GitHub pull requests')
+ .get('/issues-pr/badges/shields.json')
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('GitHub pull requests preserve repository redirects')
+ .get('/issues-pr/facebook/react.json')
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('GitHub pull requests raw')
+ .get('/issues-pr-raw/badges/shields.json')
+ .expectBadge({
+ label: 'open pull requests',
+ message: isMetric,
+ })
+
+t.create('GitHub closed pull requests')
+ .get('/issues-pr-closed/badges/shields.json')
+ .expectBadge({
+ label: 'pull requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('GitHub closed pull requests raw')
+ .get('/issues-pr-closed-raw/badges/shields.json')
+ .expectBadge({
+ label: 'closed pull requests',
+ message: isMetric,
+ })
+
+t.create('GitHub closed pull requests raw with only drafts')
+ .get('/issues-pr-closed-raw/badges/shields.json?onlyDrafts')
+ .expectBadge({
+ label: 'closed draft pull requests',
+ message: Joi.alternatives(isMetric, Joi.equal('0')),
+ })
+
+t.create('GitHub pull requests excluding drafts')
+ .get('/issues-pr/badges/shields.json?excludeDrafts')
+ .expectBadge({
+ label: 'non-draft pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('GitHub pull requests with only drafts')
+ .get('/issues-pr/badges/shields.json?onlyDrafts')
+ .expectBadge({
+ label: 'draft pull requests',
+ message: Joi.alternatives(isMetricOpenIssues, Joi.equal('0 open')),
+ })
+
+t.create('GitHub closed pull requests by multi-word label excluding drafts')
+ .get(
+ '/issues-pr-closed/badges/shields/good%20first%20issue.json?excludeDrafts',
+ )
+ .expectBadge({
+ label: '"good first issue" non-draft pull requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('GitHub pull requests with conflicting draft filters')
+ .get('/issues-pr/badges/shields.json?excludeDrafts&onlyDrafts')
+ .expectBadge({
+ label: 'pull requests',
+ message: Joi.string().pattern(/^invalid query parameter/),
+ })
+
+t.create('GitHub pull requests (repo not found)')
+ .get('/issues-pr-raw/badges/helmets.json')
+ .expectBadge({
+ label: 'pull requests',
+ message: 'repo not found',
+ })
+
+t.create('GitHub open pull requests by label')
+ .get('/issues-pr/badges/shields/service-badge.json')
+ .expectBadge({
+ label: 'service-badge pull requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('GitHub open pull requests by label (raw)')
+ .get('/issues-pr-raw/badges/shields/service-badge.json')
+ .expectBadge({
+ label: 'open service-badge pull requests',
+ message: isMetric,
+ })
diff --git a/services/github/github-r-package.service.js b/services/github/github-r-package.service.js
new file mode 100644
index 0000000000000..566a773067dc1
--- /dev/null
+++ b/services/github/github-r-package.service.js
@@ -0,0 +1,93 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
+import { fetchRepoContent } from './github-common-fetch.js'
+import { documentation } from './github-helpers.js'
+
+const queryParamSchema = Joi.object({
+ filename: Joi.string(),
+}).required()
+
+const versionRegExp = /^Version:[\s]*(.+)$/m
+
+const filenameDescription =
+ 'The `filename` param can be used to specify the path to `DESCRIPTION`. By default, we look for `DESCRIPTION` in the repo root'
+
+export default class GithubRPackageVersion extends ConditionalGithubAuthV3Service {
+ static category = 'version'
+
+ static route = {
+ base: 'github/r-package/v',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/r-package/v/{user}/{repo}': {
+ get: {
+ summary: 'GitHub R package version',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'mixOmicsTeam' }),
+ pathParam({ name: 'repo', example: 'mixOmics' }),
+ queryParam({
+ name: 'filename',
+ example: 'subdirectory/DESCRIPTION',
+ description: filenameDescription,
+ }),
+ ],
+ },
+ },
+ '/github/r-package/v/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitHub R package version (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'mixOmicsTeam' }),
+ pathParam({ name: 'repo', example: 'mixOmics' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ queryParam({
+ name: 'filename',
+ example: 'subdirectory/DESCRIPTION',
+ description: filenameDescription,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'R' }
+
+ static render({ version, branch }) {
+ return renderVersionBadge({
+ version,
+ tag: branch,
+ defaultLabel: 'R',
+ })
+ }
+
+ static transform(content, filename) {
+ const match = versionRegExp.exec(content)
+ if (!match) {
+ throw new InvalidResponse({
+ prettyMessage: `Version missing in ${filename}`,
+ })
+ }
+
+ return {
+ version: match[1],
+ }
+ }
+
+ async handle({ user, repo, branch }, { filename = 'DESCRIPTION' }) {
+ const content = await fetchRepoContent(this, {
+ user,
+ repo,
+ branch,
+ filename,
+ })
+ const { version } = this.constructor.transform(content, filename)
+ return this.constructor.render({ version, branch })
+ }
+}
diff --git a/services/github/github-r-package.spec.js b/services/github/github-r-package.spec.js
new file mode 100644
index 0000000000000..23828d5b745cc
--- /dev/null
+++ b/services/github/github-r-package.spec.js
@@ -0,0 +1,28 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import GithubRPackageVersion from './github-r-package.service.js'
+
+describe('GithubRPackageVersion', function () {
+ const content = versionLine =>
+ [
+ 'Package: mypackage',
+ 'Title: What The Package Does (one line, title case required)',
+ versionLine,
+ '',
+ ].join('\n')
+
+ test(GithubRPackageVersion.transform, () => {
+ given(content('Version: 6.10.9'), 'DESCRIPTION').expect({
+ version: '6.10.9',
+ })
+ })
+
+ it('throws InvalidResponse if a file does not contain version specification', function () {
+ expect(() =>
+ GithubRPackageVersion.transform(content('Versio: 6.10.9'), 'DESCRIPTION'),
+ )
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'Version missing in DESCRIPTION')
+ })
+})
diff --git a/services/github/github-r-package.tester.js b/services/github/github-r-package.tester.js
new file mode 100644
index 0000000000000..7f499b651d072
--- /dev/null
+++ b/services/github/github-r-package.tester.js
@@ -0,0 +1,33 @@
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('R package version').get('/mixOmicsTeam/mixOmics.json').expectBadge({
+ label: 'R',
+ message: isVPlusDottedVersionAtLeastOne,
+})
+
+t.create('R package version (from branch)')
+ .get('/mixOmicsTeam/mixOmics/master.json')
+ .expectBadge({
+ label: 'R@master',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
+t.create('R package version (monorepo)')
+ .get(
+ `/wch/r-source.json?filename=${encodeURIComponent(
+ 'src/gnuwin32/windlgs/DESCRIPTION',
+ )}`,
+ )
+ .expectBadge({
+ label: 'R',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
+t.create('R package version (repo not found)')
+ .get('/badges/not-existing-repo.json')
+ .expectBadge({
+ label: 'R',
+ message: 'repo not found, branch not found, or DESCRIPTION missing',
+ })
diff --git a/services/github/github-release-date.service.js b/services/github/github-release-date.service.js
index 4685b86afd252..4d61429843d44 100644
--- a/services/github/github-release-date.service.js
+++ b/services/github/github-release-date.service.js
@@ -1,75 +1,65 @@
-'use strict'
-
-const moment = require('moment')
-const Joi = require('@hapi/joi')
-const { age } = require('../color-formatters')
-const { formatDate } = require('../text-formatters')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.alternatives(
Joi.object({
created_at: Joi.date().required(),
+ published_at: Joi.date().required(),
}).required(),
Joi.array()
.items(
Joi.object({
created_at: Joi.date().required(),
- }).required()
+ published_at: Joi.date().required(),
+ }).required(),
)
- .min(1)
+ .min(1),
)
-module.exports = class GithubReleaseDate extends GithubAuthV3Service {
- static get category() {
- return 'activity'
- }
+const displayDateEnum = ['created_at', 'published_at']
- static get route() {
- return {
- base: 'github',
- pattern: ':variant(release-date|release-date-pre)/:user/:repo',
- }
- }
+const queryParamSchema = Joi.object({
+ display_date: Joi.string()
+ .valid(...displayDateEnum)
+ .default('published_at'),
+}).required()
- static get examples() {
- return [
- {
- title: 'GitHub Release Date',
- pattern: 'release-date/:user/:repo',
- namedParams: {
- user: 'SubtitleEdit',
- repo: 'subtitleedit',
- },
- staticPreview: this.render({ date: '2017-04-13T07:50:27.000Z' }),
- documentation,
- },
- {
- title: 'GitHub (Pre-)Release Date',
- pattern: 'release-date-pre/:user/:repo',
- namedParams: {
- user: 'Cockatrice',
- repo: 'Cockatrice',
- },
- staticPreview: this.render({ date: '2017-04-13T07:50:27.000Z' }),
- documentation,
- },
- ]
+export default class GithubReleaseDate extends GithubAuthV3Service {
+ static category = 'activity'
+ static route = {
+ base: 'github',
+ pattern: ':variant(release-date|release-date-pre)/:user/:repo',
+ queryParamSchema,
}
- static get defaultBadgeData() {
- return {
- label: 'release date',
- }
+ static openApi = {
+ '/github/{variant}/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Release Date',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'variant',
+ example: 'release-date',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ }),
+ pathParam({ name: 'user', example: 'SubtitleEdit' }),
+ pathParam({ name: 'repo', example: 'subtitleedit' }),
+ queryParam({
+ name: 'display_date',
+ example: 'published_at',
+ schema: { type: 'string', enum: displayDateEnum },
+ description: 'Default value is `created_at` if not specified',
+ }),
+ ],
+ },
+ },
}
- static render({ date }) {
- const releaseDate = moment(date)
- return {
- message: formatDate(releaseDate),
- color: age(releaseDate),
- }
- }
+ static defaultBadgeData = { label: 'release date' }
async fetch({ variant, user, repo }) {
const url =
@@ -79,15 +69,15 @@ module.exports = class GithubReleaseDate extends GithubAuthV3Service {
return this._requestJson({
url,
schema,
- errorMessages: errorMessagesFor('no releases or repo not found'),
+ httpErrors: httpErrorsFor('no releases or repo not found'),
})
}
- async handle({ variant, user, repo }) {
+ async handle({ variant, user, repo }, queryParams) {
const body = await this.fetch({ variant, user, repo })
if (Array.isArray(body)) {
- return this.constructor.render({ date: body[0].created_at })
+ return renderDateBadge(body[0][queryParams.display_date])
}
- return this.constructor.render({ date: body.created_at })
+ return renderDateBadge(body[queryParams.display_date])
}
}
diff --git a/services/github/github-release-date.tester.js b/services/github/github-release-date.tester.js
index 314edaca0ae3c..eb0033d637f9c 100644
--- a/services/github/github-release-date.tester.js
+++ b/services/github/github-release-date.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isFormattedDate } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Release Date. e.g release date|today')
.get('/release-date/mochajs/mocha.json')
@@ -10,8 +9,29 @@ t.create('Release Date. e.g release date|today')
message: isFormattedDate,
})
+t.create('Release Date - display_date by `created_at` (default)')
+ .get('/release-date/microsoft/vscode.json?display_date=created_at')
+ .expectBadge({
+ label: 'release date',
+ message: isFormattedDate,
+ })
+
+t.create('Release Date - display_date by `published_at`')
+ .get('/release-date/microsoft/vscode.json?display_date=published_at')
+ .expectBadge({
+ label: 'release date',
+ message: isFormattedDate,
+ })
+
+t.create('Release Date - display_date by `published_at`, incorrect query param')
+ .get('/release-date/microsoft/vscode.json?display_date=published_attttttttt')
+ .expectBadge({
+ label: 'release date',
+ message: 'invalid query parameter: display_date',
+ })
+
t.create(
- 'Release Date - Should return `no releases or repo not found` for invalid repo'
+ 'Release Date - Should return `no releases or repo not found` for invalid repo',
)
.get('/release-date/not-valid-name/not-valid-repo.json')
.expectBadge({
@@ -27,7 +47,7 @@ t.create('(Pre-)Release Date. e.g release date|today')
})
t.create(
- '(Pre-)Release Date - Should return `no releases or repo not found` for invalid repo'
+ '(Pre-)Release Date - Should return `no releases or repo not found` for invalid repo',
)
.get('/release-date-pre/not-valid-name/not-valid-repo.json')
.expectBadge({
diff --git a/services/github/github-release.service.js b/services/github/github-release.service.js
index 74886c9813a14..aaed99ebea9fa 100644
--- a/services/github/github-release.service.js
+++ b/services/github/github-release.service.js
@@ -1,101 +1,72 @@
-'use strict'
-
-const { addv } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const { redirector } = require('..')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const {
+import Joi from 'joi'
+import { redirector, pathParam, queryParam } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import {
fetchLatestRelease,
queryParamSchema,
-} = require('./github-common-release')
-const { documentation } = require('./github-helpers')
+ openApiQueryParams,
+} from './github-common-release.js'
+import { documentation } from './github-helpers.js'
-class GithubRelease extends GithubAuthV3Service {
- static get category() {
- return 'version'
- }
+const displayNameEnum = ['tag', 'release']
+const extendedQueryParamSchema = Joi.object({
+ display_name: Joi.string()
+ .valid(...displayNameEnum)
+ .default('tag'),
+})
- static get route() {
- return {
- base: 'github/v/release',
- pattern: ':user/:repo',
- queryParamSchema,
- }
+class GithubRelease extends GithubAuthV3Service {
+ static category = 'version'
+ static route = {
+ base: 'github/v/release',
+ pattern: ':user/:repo',
+ queryParamSchema: queryParamSchema.concat(extendedQueryParamSchema),
}
- static get examples() {
- return [
- {
- title: 'GitHub release (latest by date)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: {},
- staticPreview: this.render({
- version: 'v4.16.4',
- sort: 'date',
- isPrerelease: false,
- }),
- documentation,
- },
- {
- title: 'GitHub release (latest by date including pre-releases)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: { include_prereleases: null },
- staticPreview: this.render({
- version: 'v5.0.0-alpha.7',
- sort: 'date',
- isPrerelease: true,
- }),
- documentation,
+ static openApi = {
+ '/github/v/release/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Release',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'expressjs' }),
+ pathParam({ name: 'repo', example: 'express' }),
+ ...openApiQueryParams,
+ queryParam({
+ name: 'display_name',
+ example: 'tag',
+ schema: { type: 'string', enum: displayNameEnum },
+ }),
+ ],
},
- {
- title: 'GitHub release (latest SemVer)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: { sort: 'semver' },
- staticPreview: this.render({
- version: 'v4.16.4',
- sort: 'semver',
- isPrerelease: false,
- }),
- documentation,
- },
- {
- title: 'GitHub release (latest SemVer including pre-releases)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: { sort: 'semver', include_prereleases: null },
- staticPreview: this.render({
- version: 'v5.0.0-alpha.7',
- sort: 'semver',
- isPrerelease: true,
- }),
- documentation,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'release',
- namedLogo: 'github',
+ static defaultBadgeData = { label: 'release' }
+
+ static transform(latestRelease, display) {
+ const { name, tag_name: tagName, prerelease: isPrerelease } = latestRelease
+ if (display === 'tag') {
+ return { isPrerelease, version: tagName }
}
- }
- static render({ version, sort, isPrerelease }) {
- let color = 'blue'
- color = sort === 'semver' ? versionColor(version) : color
- color = isPrerelease ? 'orange' : color
- return { message: addv(version), color }
+ return { version: name || tagName, isPrerelease }
}
async handle({ user, repo }, queryParams) {
const latestRelease = await fetchLatestRelease(
this,
{ user, repo },
- queryParams
+ queryParams,
+ )
+ const { version, isPrerelease } = this.constructor.transform(
+ latestRelease,
+ queryParams.display_name,
)
- return this.constructor.render({
- version: latestRelease.tag_name,
- sort: queryParams.sort,
- isPrerelease: latestRelease.prerelease,
+ return renderVersionBadge({
+ version,
+ isPrerelease,
})
}
}
@@ -134,7 +105,4 @@ const redirects = {
}),
}
-module.exports = {
- GithubRelease,
- ...redirects,
-}
+export { GithubRelease, redirects }
diff --git a/services/github/github-release.spec.js b/services/github/github-release.spec.js
new file mode 100644
index 0000000000000..dc63a49216189
--- /dev/null
+++ b/services/github/github-release.spec.js
@@ -0,0 +1,25 @@
+import { test, given } from 'sazerac'
+import { GithubRelease } from './github-release.service.js'
+
+describe('GithubRelease', function () {
+ test(GithubRelease.transform, () => {
+ given({ name: null, tag_name: '0.1.2', prerelease: true }, 'tag').expect({
+ version: '0.1.2',
+ isPrerelease: true,
+ })
+ given(
+ { name: null, tag_name: '0.1.3', prerelease: true },
+ 'release',
+ ).expect({
+ version: '0.1.3',
+ isPrerelease: true,
+ })
+ given(
+ { name: 'fun name', tag_name: '1.0.0', prerelease: false },
+ 'release',
+ ).expect({
+ version: 'fun name',
+ isPrerelease: false,
+ })
+ })
+})
diff --git a/services/github/github-release.tester.js b/services/github/github-release.tester.js
index 7042c1f7f835e..e7fdb632db731 100644
--- a/services/github/github-release.tester.js
+++ b/services/github/github-release.tester.js
@@ -1,14 +1,12 @@
-'use strict'
+import Joi from 'joi'
+import { isSemver } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { isSemver } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubRelease',
title: 'Github Release',
pathPrefix: '/github',
-}))
+})
t.create('Release')
.get('/v/release/expressjs/express.json')
@@ -19,18 +17,22 @@ t.create('Prerelease')
.expectBadge({
label: 'release',
message: isSemver,
- color: Joi.string()
- .allow('blue', 'orange')
- .required(),
+ color: Joi.equal('blue', 'orange').required(),
})
+// basic query parameter testing. application of param in transform
+// logic is tested via unit tests in github-release.spec.js
+t.create('Release (release name instead of tag name)')
+ .get('/v/release/expressjs/express.json?display_name=release')
+ .expectBadge({ label: 'release', message: isSemver, color: 'blue' })
+
t.create('Release (No releases)')
.get('/v/release/badges/daily-tests.json')
.expectBadge({ label: 'release', message: 'no releases or repo not found' })
t.create('Prerelease (No releases)')
.get('/v/release/badges/daily-tests.json?include_prereleases')
- .expectBadge({ label: 'release', message: 'no releases' })
+ .expectBadge({ label: 'release', message: 'no releases found' })
t.create('Release (repo not found)')
.get('/v/release/badges/helmets.json')
@@ -44,11 +46,11 @@ t.create('Release (legacy route: release)')
t.create('(pre-)Release (legacy route: release/all)')
.get('/release/photonstorm/phaser/all.svg')
.expectRedirect(
- '/github/v/release/photonstorm/phaser.svg?include_prereleases'
+ '/github/v/release/photonstorm/phaser.svg?include_prereleases',
)
t.create('(pre-)Release (legacy route: release-pre)')
.get('/release-pre/photonstorm/phaser.svg')
.expectRedirect(
- '/github/v/release/photonstorm/phaser.svg?include_prereleases'
+ '/github/v/release/photonstorm/phaser.svg?include_prereleases',
)
diff --git a/services/github/github-repo-size.service.js b/services/github/github-repo-size.service.js
index a16a3fe5ee24d..75fd71dc677d0 100644
--- a/services/github/github-repo-size.service.js
+++ b/services/github/github-repo-size.service.js
@@ -1,65 +1,50 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderSizeBadge } from '../size.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
size: nonNegativeInteger,
}).required()
-module.exports = class GithubRepoSize extends GithubAuthV3Service {
- static get category() {
- return 'size'
- }
-
- static get route() {
- return {
- base: 'github/repo-size',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub repo size',
- namedParams: {
- user: 'atom',
- repo: 'atom',
- },
- staticPreview: this.render({ size: 319488 }),
- documentation,
+export default class GithubRepoSize extends GithubAuthV3Service {
+ static category = 'size'
+ static route = { base: 'github/repo-size', pattern: ':user/:repo' }
+ static openApi = {
+ '/github/repo-size/{user}/{repo}': {
+ get: {
+ summary: 'GitHub repo size',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'atom',
+ },
+ {
+ name: 'repo',
+ example: 'atom',
+ },
+ ),
},
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'repo size',
- }
+ },
}
- static render({ size }) {
- return {
- // note the GH API returns size in Kb
- message: prettyBytes(size * 1024),
- color: 'blue',
- }
- }
+ static defaultBadgeData = { label: 'repo size' }
async fetch({ user, repo }) {
return this._requestJson({
url: `/repos/${user}/${repo}`,
schema,
- errorMessages: errorMessagesFor(),
+ httpErrors: httpErrorsFor(),
})
}
async handle({ user, repo }) {
const { size } = await this.fetch({ user, repo })
- return this.constructor.render({ size })
+ // note the GH API returns size in KiB
+ // so we multiply by 1024 to get a size in bytes and then format that in IEC bytes
+ return renderSizeBadge(size * 1024, 'iec', 'repo size')
}
}
diff --git a/services/github/github-repo-size.tester.js b/services/github/github-repo-size.tester.js
index cc55e28378dbd..7de73bc446dcd 100644
--- a/services/github/github-repo-size.tester.js
+++ b/services/github/github-repo-size.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isIecFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('repository size')
- .get('/badges/shields.json')
- .expectBadge({
- label: 'repo size',
- message: isFileSize,
- })
+t.create('repository size').get('/badges/shields.json').expectBadge({
+ label: 'repo size',
+ message: isIecFileSize,
+})
t.create('repository size (repo not found)')
.get('/badges/helmets.json')
diff --git a/services/github/github-search.service.js b/services/github/github-search.service.js
index 4393feff170c4..8d89cada76fd9 100644
--- a/services/github/github-search.service.js
+++ b/services/github/github-search.service.js
@@ -1,45 +1,47 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { errorMessagesFor, documentation } = require('./github-helpers')
+import Joi from 'joi'
+import { queryParams, redirector } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation } from './github-helpers.js'
const schema = Joi.object({ total_count: nonNegativeInteger }).required()
-module.exports = class GithubSearch extends GithubAuthV3Service {
- static get category() {
- return 'analysis'
- }
+const queryParamSchema = Joi.object({
+ query: Joi.string().required(),
+}).required()
- static get route() {
- return {
- base: 'github/search',
- pattern: ':user/:repo/:query+',
- }
+const codeSearchDocs = `
+For a full list of available filters and allowed values,
+see GitHub's documentation on
+[Searching code](https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax)`
+
+class GitHubCodeSearch extends GithubAuthV3Service {
+ static category = 'analysis'
+
+ static route = {
+ base: 'github',
+ pattern: 'search',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub search hit counter',
- pattern: ':user/:repo/:query',
- namedParams: {
- user: 'torvalds',
- repo: 'linux',
- query: 'goto',
- },
- staticPreview: this.render({ query: 'goto', totalCount: 14000 }),
- documentation,
+ static openApi = {
+ '/github/search': {
+ get: {
+ summary: 'GitHub code search count',
+ description: documentation,
+ parameters: queryParams({
+ name: 'query',
+ description: codeSearchDocs,
+ example: 'goto language:javascript NOT is:fork NOT is:archived',
+ required: true,
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'counter',
- }
+ static defaultBadgeData = {
+ label: 'counter',
}
static render({ query, totalCount }) {
@@ -50,17 +52,35 @@ module.exports = class GithubSearch extends GithubAuthV3Service {
}
}
- async handle({ user, repo, query }) {
+ async handle(_routeParams, { query }) {
const { total_count: totalCount } = await this._requestJson({
url: '/search/code',
options: {
- qs: {
- q: `${query} repo:${user}/${repo}`,
+ searchParams: {
+ q: query,
},
},
schema,
- errorMessages: errorMessagesFor('repo not found'),
+ httpErrors: {
+ 401: 'auth required for search api',
+ },
})
+
return this.constructor.render({ query, totalCount })
}
}
+
+const GitHubCodeSearchRedirect = redirector({
+ category: 'analysis',
+ route: {
+ base: 'github/search',
+ pattern: ':user/:repo/:query+',
+ },
+ transformPath: () => '/github/search',
+ transformQueryParams: ({ query, user, repo }) => ({
+ query: `${query} repo:${user}/${repo}`,
+ }),
+ dateAdded: new Date('2024-11-29'),
+})
+
+export { GitHubCodeSearch, GitHubCodeSearchRedirect }
diff --git a/services/github/github-search.tester.js b/services/github/github-search.tester.js
index 83dd403194404..060eb4766ea39 100644
--- a/services/github/github-search.tester.js
+++ b/services/github/github-search.tester.js
@@ -1,12 +1,20 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('hit counter')
- .get('/badges/shields/async%20handle.json')
+ .get('/search.json?query=async%20handle')
.expectBadge({ label: 'async handle counter', message: isMetric })
-t.create('hit counter for nonexistent repo')
- .get('/badges/puppets/async%20handle.json')
- .expectBadge({ label: 'counter', message: 'repo not found' })
+t.create('hit counter, zero results')
+ .get('/search.json?query=async%20handle%20repo%3Abadges%2Fpuppets')
+ .expectBadge({
+ label: 'async handle repo:badges/puppets counter',
+ message: '0',
+ })
+
+t.create('legacy redirect')
+ .get('/search/badges/shields/async%20handle.svg')
+ .expectRedirect(
+ '/github/search.svg?query=async%20handle%20repo%3Abadges%2Fshields',
+ )
diff --git a/services/github/github-size.service.js b/services/github/github-size.service.js
index 76a6b59705462..3b28e4b7d7ca9 100644
--- a/services/github/github-size.service.js
+++ b/services/github/github-size.service.js
@@ -1,67 +1,71 @@
-'use strict'
+import Joi from 'joi'
+import { renderSizeBadge } from '../size.js'
+import { nonNegativeInteger } from '../validators.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { nonNegativeInteger } = require('../validators')
-const { NotFound } = require('..')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+const queryParamSchema = Joi.object({
+ branch: Joi.string(),
+}).required()
const schema = Joi.alternatives(
Joi.object({
size: nonNegativeInteger,
}).required(),
- Joi.array().required()
+ Joi.array().required(),
)
-module.exports = class GithubSize extends GithubAuthV3Service {
- static get category() {
- return 'size'
- }
+export default class GithubSize extends GithubAuthV3Service {
+ static category = 'size'
- static get route() {
- return {
- base: 'github/size',
- pattern: ':user/:repo/:path*',
- }
+ static route = {
+ base: 'github/size',
+ pattern: ':user/:repo/:path+',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub file size in bytes',
- namedParams: {
- user: 'webcaetano',
- repo: 'craft',
- path: 'build/phaser-craft.min.js',
- },
- staticPreview: this.render({ size: 9170 }),
- keywords: ['repo'],
- documentation,
+ static openApi = {
+ '/github/size/{user}/{repo}/{path}': {
+ get: {
+ summary: 'GitHub file size in bytes',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'webcaetano' }),
+ pathParam({ name: 'repo', example: 'craft' }),
+ pathParam({ name: 'path', example: 'build/phaser-craft.min.js' }),
+ queryParam({
+ name: 'branch',
+ example: 'master',
+ description: 'Can be a branch, a tag or a commit hash.',
+ }),
+ ],
},
- ]
+ },
}
- static render({ size }) {
- return {
- message: prettyBytes(size),
- color: 'blue',
+ async fetch({ user, repo, path, branch }) {
+ if (branch) {
+ return this._requestJson({
+ url: `/repos/${user}/${repo}/contents/${path}?ref=${branch}`,
+ schema,
+ httpErrors: httpErrorsFor('repo, branch or file not found'),
+ })
+ } else {
+ return this._requestJson({
+ url: `/repos/${user}/${repo}/contents/${path}`,
+ schema,
+ httpErrors: httpErrorsFor('repo or file not found'),
+ })
}
}
- async fetch({ user, repo, path }) {
- return this._requestJson({
- url: `/repos/${user}/${repo}/contents/${path}`,
- schema,
- errorMessages: errorMessagesFor('repo or file not found'),
- })
- }
-
- async handle({ user, repo, path }) {
- const body = await this.fetch({ user, repo, path })
+ async handle({ user, repo, path }, queryParams) {
+ const branch = queryParams.branch
+ const body = await this.fetch({ user, repo, path, branch })
if (Array.isArray(body)) {
throw new NotFound({ prettyMessage: 'not a regular file' })
}
- return this.constructor.render({ size: body.size })
+ return renderSizeBadge(body.size, 'iec')
}
}
diff --git a/services/github/github-size.tester.js b/services/github/github-size.tester.js
index 9645a469eee22..5a425112e3cd7 100644
--- a/services/github/github-size.tester.js
+++ b/services/github/github-size.tester.js
@@ -1,16 +1,31 @@
-'use strict'
-
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIecFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('File size')
.get('/webcaetano/craft/build/phaser-craft.min.js.json')
- .expectBadge({ label: 'size', message: isFileSize })
+ .expectBadge({ label: 'size', message: isIecFileSize })
t.create('File size 404')
.get('/webcaetano/craft/build/does-not-exist.min.js.json')
.expectBadge({ label: 'size', message: 'repo or file not found' })
+t.create('File size for nonexisting branch')
+ .get('/webcaetano/craft/build/phaser-craft.min.js.json?branch=notARealBranch')
+ .expectBadge({ label: 'size', message: 'repo, branch or file not found' })
+
t.create('File size for "not a regular file"')
.get('/webcaetano/craft/build.json')
.expectBadge({ label: 'size', message: 'not a regular file' })
+
+t.create('File size for a specified branch')
+ .get('/webcaetano/craft/build/craft.min.js.json?branch=version-2')
+ .expectBadge({ label: 'size', message: isIecFileSize })
+
+t.create('File size for a specified tag')
+ .get('/webcaetano/craft/build/phaser-craft.min.js.json?branch=2.1.2')
+ .expectBadge({ label: 'size', message: isIecFileSize })
+
+t.create('File size for a specified commit')
+ .get('/webcaetano/craft/build/phaser-craft.min.js.json?branch=b848dbb')
+ .expectBadge({ label: 'size', message: isIecFileSize })
diff --git a/services/github/github-sponsors.service.js b/services/github/github-sponsors.service.js
new file mode 100644
index 0000000000000..88e112a323de3
--- /dev/null
+++ b/services/github/github-sponsors.service.js
@@ -0,0 +1,86 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { NotFound, pathParams } from '../index.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import { documentation, transformErrors } from './github-helpers.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ repositoryOwner: Joi.object({
+ sponsorshipsAsMaintainer: Joi.object({
+ totalCount: nonNegativeInteger,
+ }),
+ }).allow(null),
+ }).required(),
+}).required()
+
+export default class GithubSponsors extends GithubAuthV4Service {
+ static category = 'funding'
+ static route = { base: 'github/sponsors', pattern: ':user' }
+ static openApi = {
+ '/github/sponsors/{user}': {
+ get: {
+ summary: 'GitHub Sponsors',
+ description: documentation,
+ parameters: pathParams({
+ name: 'user',
+ example: 'Homebrew',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'sponsors',
+ }
+
+ static render({ count }) {
+ return {
+ message: metric(count),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ user }) {
+ return this._requestGraphql({
+ query: gql`
+ query ($user: String!) {
+ repositoryOwner(login: $user) {
+ ... on User {
+ sponsorshipsAsMaintainer(includePrivate: true) {
+ totalCount
+ }
+ }
+ ... on Organization {
+ sponsorshipsAsMaintainer(includePrivate: true) {
+ totalCount
+ }
+ }
+ }
+ }
+ `,
+ variables: { user },
+ schema,
+ transformErrors,
+ })
+ }
+
+ transform({ data }) {
+ if (data.repositoryOwner == null) {
+ throw new NotFound({ prettyMessage: 'user/org not found' })
+ }
+
+ const count = data.repositoryOwner.sponsorshipsAsMaintainer.totalCount
+ return { count }
+ }
+
+ async handle({ user }) {
+ const json = await this.fetch({ user })
+ const { count } = this.transform({ data: json.data })
+ return this.constructor.render({
+ count,
+ })
+ }
+}
diff --git a/services/github/github-sponsors.tester.js b/services/github/github-sponsors.tester.js
new file mode 100644
index 0000000000000..dc5247cc4e1d5
--- /dev/null
+++ b/services/github/github-sponsors.tester.js
@@ -0,0 +1,21 @@
+import { isMetric } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'GithubSponsors',
+ title: 'Github Sponsors',
+ pathPrefix: '/github',
+})
+
+t.create('Sponsors').get('/sponsors/Homebrew.json').expectBadge({
+ label: 'sponsors',
+ message: isMetric,
+ color: 'blue',
+})
+
+t.create('Sponsors (user not found)')
+ .get('/sponsors/badges-non-exist.json')
+ .expectBadge({
+ label: 'sponsors',
+ message: 'user/org not found',
+ })
diff --git a/services/github/github-stars.service.js b/services/github/github-stars.service.js
index c4592a6d7e885..15a951df80b34 100644
--- a/services/github/github-stars.service.js
+++ b/services/github/github-stars.service.js
@@ -1,62 +1,49 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
stargazers_count: nonNegativeInteger,
}).required()
-module.exports = class GithubStars extends GithubAuthV3Service {
- static get category() {
- return 'social'
- }
+export default class GithubStars extends GithubAuthV3Service {
+ static category = 'social'
- static get route() {
- return {
- base: 'github/stars',
- pattern: ':user/:repo',
- }
+ static route = {
+ base: 'github/stars',
+ pattern: ':user/:repo',
}
- static get examples() {
- return [
- {
- title: 'GitHub stars',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- queryParams: { style: 'social' },
- // TODO: This is currently a literal, as `staticPreview` doesn't
- // support `link`.
- staticPreview: {
- label: 'Stars',
- message: '7k',
- style: 'social',
- },
- documentation,
+ static openApi = {
+ '/github/stars/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Repo stars',
+ description: documentation,
+ parameters: pathParams(
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'stars',
- namedLogo: 'github',
- }
+ static defaultBadgeData = {
+ label: 'stars',
+ namedLogo: 'github',
}
static render({ stars, user, repo }) {
+ const slug = `${encodeURIComponent(user)}/${encodeURIComponent(repo)}`
return {
message: metric(stars),
+ style: 'social',
color: 'blue',
link: [
- `https://github.com/${user}/${repo}`,
- `https://github.com/${user}/${repo}/stargazers`,
+ `https://github.com/${slug}`,
+ `https://github.com/${slug}/stargazers`,
],
}
}
@@ -65,7 +52,7 @@ module.exports = class GithubStars extends GithubAuthV3Service {
const { stargazers_count: stars } = await this._requestJson({
url: `/repos/${user}/${repo}`,
schema,
- errorMessages: errorMessagesFor(),
+ httpErrors: httpErrorsFor(),
})
return this.constructor.render({ user, repo, stars })
}
diff --git a/services/github/github-stars.tester.js b/services/github/github-stars.tester.js
index 96bb123407b69..6155dd553baeb 100644
--- a/services/github/github-stars.tester.js
+++ b/services/github/github-stars.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Stars')
.get('/badges/shields.json')
@@ -14,9 +13,21 @@ t.create('Stars')
],
})
-t.create('Stars (repo not found)')
- .get('/badges/helmets.json')
+t.create('Stars (repo not found)').get('/badges/helmets.json').expectBadge({
+ label: 'stars',
+ message: 'repo not found',
+})
+
+// https://github.com/badges/shields/issues/4982
+// It doesn't seem important that this work with trailing spaces, though if it
+// did we'd probably want a more global fix.
+t.create('Stars (repo name with trailing spaces)')
+ .get('/badges/shields .json')
.expectBadge({
label: 'stars',
- message: 'repo not found',
+ message: isMetric,
+ link: [
+ 'https://github.com/badges/shields%20%20%20%20%20',
+ 'https://github.com/badges/shields%20%20%20%20%20/stargazers',
+ ],
})
diff --git a/services/github/github-tag.service.js b/services/github/github-tag.service.js
index ebad4f1f78d4c..42a9ced1e7f66 100644
--- a/services/github/github-tag.service.js
+++ b/services/github/github-tag.service.js
@@ -1,14 +1,14 @@
-'use strict'
-
-const gql = require('graphql-tag')
-const Joi = require('@hapi/joi')
-const { addv } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const { latest } = require('../version')
-const { NotFound, redirector } = require('..')
-const { GithubAuthV4Service } = require('./github-auth-service')
-const { queryParamSchema } = require('./github-common-release')
-const { documentation, transformErrors } = require('./github-helpers')
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { matcher } from 'matcher'
+import { latest, renderVersionBadge } from '../version.js'
+import { NotFound, redirector, pathParam } from '../index.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import {
+ queryParamSchema,
+ openApiQueryParams,
+} from './github-common-release.js'
+import { documentation, transformErrors } from './github-helpers.js'
const schema = Joi.object({
data: Joi.object({
@@ -27,65 +27,50 @@ const schema = Joi.object({
}).required()
class GithubTag extends GithubAuthV4Service {
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: 'github/v/tag',
- pattern: ':user/:repo',
- queryParamSchema,
- }
+ static route = {
+ base: 'github/v/tag',
+ pattern: ':user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'GitHub tag (latest by date)',
- namedParams: { user: 'expressjs', repo: 'express' },
- staticPreview: this.render({
- version: 'v5.0.0-alpha.7',
- sort: 'date',
- }),
- documentation,
- },
- {
- title: 'GitHub tag (latest SemVer)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: { sort: 'semver' },
- staticPreview: this.render({ version: 'v4.16.4', sort: 'semver' }),
- documentation,
- },
- {
- title: 'GitHub tag (latest SemVer pre-release)',
- namedParams: { user: 'expressjs', repo: 'express' },
- queryParams: { sort: 'semver', include_prereleases: null },
- staticPreview: this.render({
- version: 'v5.0.0-alpha.7',
- sort: 'semver',
- }),
- documentation,
+ static openApi = {
+ '/github/v/tag/{user}/{repo}': {
+ get: {
+ summary: 'GitHub Tag',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'user', example: 'expressjs' }),
+ pathParam({ name: 'repo', example: 'express' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'tag',
}
- static get defaultBadgeData() {
- return { label: 'tag' }
+ static getLimit({ sort, filter }) {
+ if (!filter && sort === 'date') {
+ return 1
+ }
+ return 100
}
- static render({ version, sort }) {
- return {
- message: addv(version),
- color: sort === 'semver' ? versionColor(version) : 'blue',
+ static applyFilter({ tags, filter }) {
+ if (!filter) {
+ return tags
}
+ return matcher(tags, filter)
}
- async fetch({ user, repo, sort }) {
- const limit = sort === 'semver' ? 100 : 1
+ async fetch({ user, repo, limit }) {
return this._requestGraphql({
query: gql`
- query($user: String!, $repo: String!, $limit: Int!) {
+ query ($user: String!, $repo: String!, $limit: Int!) {
repository(owner: $user, name: $repo) {
refs(
refPrefix: "refs/tags/"
@@ -117,19 +102,24 @@ class GithubTag extends GithubAuthV4Service {
async handle({ user, repo }, queryParams) {
const sort = queryParams.sort
const includePrereleases = queryParams.include_prereleases !== undefined
+ const filter = queryParams.filter
+ const limit = this.constructor.getLimit({ sort, filter })
- const json = await this.fetch({ user, repo, sort })
- const tags = json.data.repository.refs.edges.map(edge => edge.node.name)
+ const json = await this.fetch({ user, repo, limit })
+ const tags = this.constructor.applyFilter({
+ tags: json.data.repository.refs.edges.map(edge => edge.node.name),
+ filter,
+ })
if (tags.length === 0) {
- throw new NotFound({ prettyMessage: 'no tags found' })
+ const prettyMessage = filter ? 'no matching tags found' : 'no tags found'
+ throw new NotFound({ prettyMessage })
}
- return this.constructor.render({
+ return renderVersionBadge({
version: this.constructor.getLatestTag({
tags,
sort,
includePrereleases,
}),
- sort,
})
}
}
@@ -169,7 +159,4 @@ const redirects = {
}),
}
-module.exports = {
- GithubTag,
- ...redirects,
-}
+export { GithubTag, redirects }
diff --git a/services/github/github-tag.spec.js b/services/github/github-tag.spec.js
index b03184199ec9b..9645a7f3930e9 100644
--- a/services/github/github-tag.spec.js
+++ b/services/github/github-tag.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { GithubTag } from './github-tag.service.js'
-const { test, given } = require('sazerac')
-const { GithubTag } = require('./github-tag.service')
-
-describe('GithubTag', function() {
+describe('GithubTag', function () {
test(GithubTag.getLatestTag, () => {
const tagFixture = [
'cheese', // any old string
@@ -45,14 +43,23 @@ describe('GithubTag', function() {
}).expect('1.2.0-beta')
})
- test(GithubTag.render, () => {
- given({ usingSemver: false, version: '1.2.3' }).expect({
- message: 'v1.2.3',
- color: 'blue',
- })
- given({ usingSemver: true, version: '2.0.0' }).expect({
- message: 'v2.0.0',
- color: 'blue',
- })
+ test(GithubTag.getLimit, () => {
+ given({ sort: 'date', filter: undefined }).expect(1)
+ given({ sort: 'date', filter: '' }).expect(1)
+ given({ sort: 'date', filter: '!*-dev' }).expect(100)
+ given({ sort: 'semver', filter: undefined }).expect(100)
+ given({ sort: 'semver', filter: '' }).expect(100)
+ given({ sort: 'semver', filter: '!*-dev' }).expect(100)
+ })
+
+ test(GithubTag.applyFilter, () => {
+ const tags = ['v1.1.0', 'v1.2.0', 'server-2022-01-01']
+ given({ tags, filter: undefined }).expect(tags)
+ given({ tags, filter: '' }).expect(tags)
+ given({ tags, filter: '*' }).expect(tags)
+ given({ tags, filter: '!*' }).expect([])
+ given({ tags, filter: 'foo' }).expect([])
+ given({ tags, filter: 'server-*' }).expect(['server-2022-01-01'])
+ given({ tags, filter: '!server-*' }).expect(['v1.1.0', 'v1.2.0'])
})
})
diff --git a/services/github/github-tag.tester.js b/services/github/github-tag.tester.js
index 50f1e93c00a79..2908f93bfb5fc 100644
--- a/services/github/github-tag.tester.js
+++ b/services/github/github-tag.tester.js
@@ -1,14 +1,12 @@
-'use strict'
+import Joi from 'joi'
+import { isSemver } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { isSemver } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'GithubTag',
title: 'Github Tag',
pathPrefix: '/github',
-}))
+})
t.create('Tag')
.get('/v/tag/expressjs/express.json')
@@ -19,9 +17,7 @@ t.create('Tag (inc pre-release)')
.expectBadge({
label: 'tag',
message: isSemver,
- color: Joi.string()
- .allow('blue', 'orange')
- .required(),
+ color: Joi.equal('blue', 'orange').required(),
})
t.create('Tag (no tags)')
@@ -40,7 +36,7 @@ t.create('Tag (legacy route: tag)')
t.create('Tag (legacy route: tag-pre)')
.get('/tag-pre/photonstorm/phaser.svg')
.expectRedirect(
- '/github/v/tag/photonstorm/phaser.svg?include_prereleases&sort=semver'
+ '/github/v/tag/photonstorm/phaser.svg?sort=semver&include_prereleases',
)
t.create('Tag (legacy route: tag-date)')
diff --git a/services/github/github-top-language.service.js b/services/github/github-top-language.service.js
index 1ea1364333083..8d0dfcc6f43b8 100644
--- a/services/github/github-top-language.service.js
+++ b/services/github/github-top-language.service.js
@@ -1,42 +1,36 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { BaseGithubLanguage } from './github-languages-base.js'
+import { documentation } from './github-helpers.js'
-const { BaseGithubLanguage } = require('./github-languages-base')
-const { documentation } = require('./github-helpers')
+export default class GithubTopLanguage extends BaseGithubLanguage {
+ static category = 'analysis'
-module.exports = class GithubTopLanguage extends BaseGithubLanguage {
- static get category() {
- return 'analysis'
+ static route = {
+ base: 'github/languages/top',
+ pattern: ':user/:repo',
}
- static get route() {
- return {
- base: 'github/languages/top',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub top language',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: this.render({
- language: 'javascript',
- languageSize: 99.5,
- totalSize: 100,
- }),
- documentation,
+ static openApi = {
+ '/github/languages/top/{user}/{repo}': {
+ get: {
+ summary: 'GitHub top language',
+ description: documentation,
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'badges',
+ },
+ {
+ name: 'repo',
+ example: 'shields',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'language',
- }
+ static defaultBadgeData = {
+ label: 'language',
}
static render({ language, languageSize, totalSize }) {
@@ -51,7 +45,7 @@ module.exports = class GithubTopLanguage extends BaseGithubLanguage {
const data = await this.fetch({ user, repo })
const language = Object.keys(data).reduce(
(a, b) => (data[a] > data[b] ? a : b),
- 'language'
+ 'language',
)
return this.constructor.render({
language,
diff --git a/services/github/github-top-language.tester.js b/services/github/github-top-language.tester.js
index f6d5c805db1c2..e4d84e1f5a439 100644
--- a/services/github/github-top-language.tester.js
+++ b/services/github/github-top-language.tester.js
@@ -1,14 +1,12 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isDecimalPercentage } from '../test-validators.js'
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = await createServiceTester()
-t.create('top language')
- .get('/badges/shields.json')
- .expectBadge({
- label: 'javascript',
- message: Joi.string().regex(/^([1-9]?[0-9]\.[0-9]|100\.0)%$/),
- })
+t.create('top language').get('/badges/shields.json').expectBadge({
+ label: 'javascript',
+ message: isDecimalPercentage,
+})
t.create('top language (empty repo)')
.get('/pyvesb/emptyrepo.json')
diff --git a/services/github/github-total-star.service.js b/services/github/github-total-star.service.js
new file mode 100644
index 0000000000000..a70c1cd727c8b
--- /dev/null
+++ b/services/github/github-total-star.service.js
@@ -0,0 +1,234 @@
+import Joi from 'joi'
+import gql from 'graphql-tag'
+import { pathParam, queryParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { GithubAuthV4Service } from './github-auth-service.js'
+import {
+ documentation as commonDocumentation,
+ transformErrors,
+} from './github-helpers.js'
+
+const MAX_REPO_LIMIT = 200
+
+const description = `${commonDocumentation}
+
+Note: This badge takes into account up to ${MAX_REPO_LIMIT} of the most starred repositories of given user / org.
+`
+
+const affiliationsDescription = `This param accepts three values (must be UPPER case) OWNER, COLLABORATOR, ORGANIZATION_MEMBER.
+One can pass comma separated combinations of these values (no spaces) e.g. OWNER,COLLABORATOR or OWNER,COLLABORATOR,ORGANIZATION_MEMBER.
+Default value is OWNER. See the explanation of these values here .`
+
+const pageInfoSchema = Joi.object({
+ hasNextPage: Joi.boolean().required(),
+ endCursor: Joi.string().allow(null).required(),
+}).required()
+
+const nodesSchema = Joi.array()
+ .items(
+ Joi.object({
+ stargazers: Joi.object({
+ totalCount: nonNegativeInteger,
+ }).required(),
+ }),
+ )
+ .default([])
+
+const repositoriesSchema = Joi.object({
+ pageInfo: pageInfoSchema,
+ nodes: nodesSchema,
+}).required()
+
+const schema = Joi.object({
+ data: Joi.alternatives(
+ Joi.object({
+ user: Joi.object({
+ repositories: repositoriesSchema,
+ }).required(),
+ }).required(),
+ Joi.object({
+ organization: Joi.object({
+ repositories: repositoriesSchema,
+ }).required(),
+ }).required(),
+ ).required(),
+}).required()
+
+const query = gql`
+ query fetchStars(
+ $user: String!
+ $nextCursor: String
+ $affiliations: [RepositoryAffiliation]!
+ ) {
+ user(login: $user) {
+ repositories(
+ first: 100
+ after: $nextCursor
+ ownerAffiliations: $affiliations
+ orderBy: { field: STARGAZERS, direction: DESC }
+ ) {
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ nodes {
+ stargazers {
+ totalCount
+ }
+ }
+ }
+ }
+
+ organization(login: $user) {
+ repositories(
+ first: 100
+ after: $nextCursor
+ orderBy: { field: STARGAZERS, direction: DESC }
+ ) {
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ nodes {
+ stargazers {
+ totalCount
+ }
+ }
+ }
+ }
+ }
+`
+
+const affiliationsAllowedValues = [
+ 'OWNER',
+ 'COLLABORATOR',
+ 'ORGANIZATION_MEMBER',
+]
+/**
+ * Validates affiliations should contain combination of allowed values in any order.
+ *
+ * @param {string} value affiliation current value
+ * @param {*} helpers object to construct custom error
+ *
+ * @returns {string} valiadtion error or value unchanged
+ */
+const validateAffiliations = (value, helpers) => {
+ const values = value.split(',')
+ if (values.some(e => !affiliationsAllowedValues.includes(e))) {
+ return helpers.error('any.invalid')
+ }
+ return value
+}
+
+const queryParamSchema = Joi.object({
+ affiliations: Joi.string().default('OWNER').custom(validateAffiliations),
+}).required()
+
+export default class GithubTotalStarService extends GithubAuthV4Service {
+ static defaultLabel = 'stars'
+ static category = 'social'
+
+ static route = {
+ base: 'github/stars',
+ pattern: ':user',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/github/stars/{user}': {
+ get: {
+ summary: "GitHub User's stars",
+ description,
+ parameters: [
+ pathParam({ name: 'user', example: 'chris48s' }),
+ queryParam({
+ name: 'affiliations',
+ example: 'OWNER,COLLABORATOR',
+ description: affiliationsDescription,
+ }),
+ ],
+ },
+ },
+ '/github/stars/{org}': {
+ get: {
+ summary: "GitHub Org's stars",
+ description,
+ parameters: [pathParam({ name: 'org', example: 'badges' })],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: this.defaultLabel,
+ namedLogo: 'github',
+ }
+
+ static render({ totalStars, user }) {
+ return {
+ message: metric(totalStars),
+ style: 'social',
+ color: 'blue',
+ link: [`https://github.com/${user}`],
+ }
+ }
+
+ async fetch({ user, affiliations, nextCursor }) {
+ const variables = { user, affiliations, nextCursor }
+ return await this._requestGraphql({
+ query,
+ variables,
+ schema,
+ transformJson: json =>
+ json.data.organization || json.data.user ? { data: json.data } : json,
+ transformErrors: e => transformErrors(e, 'user/org'),
+ })
+ }
+
+ transform(repos) {
+ const totalStars = repos
+ .map(element => element.stargazers.totalCount)
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
+ const lastRepo = repos.slice(-1).pop() // undefined when repos is empty
+ const hasStars = lastRepo ? lastRepo.stargazers.totalCount !== 0 : false
+ return {
+ totalStars,
+ hasStars,
+ }
+ }
+
+ async getTotalStars({ user }, { affiliations }) {
+ let grandTotalStars = 0
+ let fetchedReposCount = 0
+ let nextCursor = null
+ let hasNext
+
+ do {
+ const { data } = await this.fetch({
+ user,
+ affiliations: affiliations.split(','),
+ nextCursor,
+ })
+ const {
+ repositories: {
+ pageInfo: { hasNextPage, endCursor },
+ nodes: repos,
+ },
+ } = data.user || data.organization
+ const { totalStars, hasStars } = this.transform(repos)
+ // repos are sorted based on the stars. If last repo has zero star,
+ // no need to fire additional fetch call, as repos on next page will have zero stars only.
+ hasNext = hasNextPage && hasStars
+ nextCursor = endCursor
+ grandTotalStars += totalStars
+ fetchedReposCount += repos.length
+ } while (hasNext && fetchedReposCount < MAX_REPO_LIMIT)
+
+ return grandTotalStars
+ }
+
+ async handle({ user }, queryParams) {
+ const totalStars = await this.getTotalStars({ user }, queryParams)
+ return this.constructor.render({ totalStars, user })
+ }
+}
diff --git a/services/github/github-total-star.tester.js b/services/github/github-total-star.tester.js
new file mode 100644
index 0000000000000..cce7ab308c676
--- /dev/null
+++ b/services/github/github-total-star.tester.js
@@ -0,0 +1,68 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Stars (User)')
+ .get('/hemantsonu20.json')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ link: ['https://github.com/hemantsonu20'],
+ })
+
+t.create('Stars (User) with affiliations')
+ .get('/hemantsonu20.json?affiliations=OWNER,COLLABORATOR')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ link: ['https://github.com/hemantsonu20'],
+ })
+
+t.create('Stars (User) with all affiliations')
+ .get('/chris48s.json?affiliations=OWNER,COLLABORATOR,ORGANIZATION_MEMBER')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ link: ['https://github.com/chris48s'],
+ })
+
+t.create('Stars (User) with invalid affiliations')
+ .get('/hemantsonu20.json?affiliations=UNKNOWN')
+ .expectBadge({
+ label: 'stars',
+ message: 'invalid query parameter: affiliations',
+ link: [],
+ })
+
+t.create('Stars (User) with invalid affiliations space')
+ .get('/hemantsonu20.json?affiliations=OWNER, COLLABORATOR')
+ .expectBadge({
+ label: 'stars',
+ message: 'invalid query parameter: affiliations',
+ link: [],
+ })
+
+t.create('Stars (Org)')
+ .get('/badges.json')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric, // matches format 13k
+ link: ['https://github.com/badges'],
+ })
+
+t.create('Stars (Org) Lots of repo')
+ .timeout(15000)
+ .get('/github.json')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric, // matches format 303k
+ link: ['https://github.com/github'],
+ })
+
+t.create('Stars (User/Org) unknown user/org')
+ .get('/badges-fake.json')
+ .expectBadge({
+ label: 'stars',
+ message: 'user/org not found',
+ link: [],
+ })
diff --git a/services/github/github-watchers.service.js b/services/github/github-watchers.service.js
index 813f62e226799..624d8aa9aead4 100644
--- a/services/github/github-watchers.service.js
+++ b/services/github/github-watchers.service.js
@@ -1,59 +1,45 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { GithubAuthV3Service } = require('./github-auth-service')
-const { documentation, errorMessagesFor } = require('./github-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { GithubAuthV3Service } from './github-auth-service.js'
+import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({
subscribers_count: nonNegativeInteger,
}).required()
-module.exports = class GithubWatchers extends GithubAuthV3Service {
- static get category() {
- return 'social'
- }
+export default class GithubWatchers extends GithubAuthV3Service {
+ static category = 'social'
- static get route() {
- return {
- base: 'github/watchers',
- pattern: ':user/:repo',
- }
+ static route = {
+ base: 'github/watchers',
+ pattern: ':user/:repo',
}
- static get examples() {
- return [
- {
- title: 'GitHub watchers',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- // TODO: This is currently a literal, as `staticPreview` doesn't
- // support `link`.
- staticPreview: {
- label: 'Watch',
- message: '96',
- style: 'social',
- },
- queryParams: { label: 'Watch' },
- documentation,
+ static openApi = {
+ '/github/watchers/{user}/{repo}': {
+ get: {
+ summary: 'GitHub watchers',
+ description: documentation,
+ parameters: pathParams(
+ { name: 'user', example: 'badges' },
+ { name: 'repo', example: 'shields' },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'watchers',
- namedLogo: 'github',
- }
+ static defaultBadgeData = {
+ label: 'watchers',
+ namedLogo: 'github',
}
static render({ watchers, user, repo }) {
return {
message: metric(watchers),
- color: '4183C4',
+ style: 'social',
+ color: 'blue',
link: [
`https://github.com/${user}/${repo}`,
`https://github.com/${user}/${repo}/watchers`,
@@ -65,7 +51,7 @@ module.exports = class GithubWatchers extends GithubAuthV3Service {
const { subscribers_count: watchers } = await this._requestJson({
url: `/repos/${user}/${repo}`,
schema,
- errorMessages: errorMessagesFor(),
+ httpErrors: httpErrorsFor(),
})
return this.constructor.render({ user, repo, watchers })
}
diff --git a/services/github/github-watchers.tester.js b/services/github/github-watchers.tester.js
index 814c7d06982fb..606b9e53c8a37 100644
--- a/services/github/github-watchers.tester.js
+++ b/services/github/github-watchers.tester.js
@@ -1,24 +1,20 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Watchers')
.get('/badges/shields.json')
.expectBadge({
label: 'watchers',
- message: Joi.number()
- .integer()
- .positive(),
+ message: Joi.number().integer().positive(),
+ color: 'blue',
link: [
'https://github.com/badges/shields',
'https://github.com/badges/shields/watchers',
],
})
-t.create('Watchers (repo not found)')
- .get('/badges/helmets.json')
- .expectBadge({
- label: 'watchers',
- message: 'repo not found',
- })
+t.create('Watchers (repo not found)').get('/badges/helmets.json').expectBadge({
+ label: 'watchers',
+ message: 'repo not found',
+})
diff --git a/services/github/github-workflow-status.service.js b/services/github/github-workflow-status.service.js
index 589b155e4580c..41004e1c67529 100644
--- a/services/github/github-workflow-status.service.js
+++ b/services/github/github-workflow-status.service.js
@@ -1,88 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService } = require('..')
-const { documentation } = require('./github-helpers')
-
-const schema = Joi.object({
- message: Joi.alternatives()
- .try(isBuildStatus, Joi.equal('no status'))
- .required(),
-}).required()
-
-const keywords = ['action', 'actions']
-
-module.exports = class GithubWorkflowStatus extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'github/workflow/status',
- pattern: ':user/:repo/:workflow/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'GitHub Workflow Status',
- pattern: ':user/:repo/:workflow',
- namedParams: {
- user: 'actions',
- repo: 'toolkit',
- workflow: 'Main workflow',
- },
- staticPreview: renderBuildStatusBadge({
- status: 'passing',
- }),
- documentation,
- keywords,
- },
- {
- title: 'GitHub Workflow Status (branch)',
- pattern: ':user/:repo/:workflow/:branch',
- namedParams: {
- user: 'actions',
- repo: 'toolkit',
- workflow: 'Main workflow',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({
- status: 'passing',
- }),
- documentation,
- keywords,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
- }
-
- async fetch({ user, repo, workflow, branch }) {
- const { message: status } = await this._requestSvg({
- schema,
- url: `https://github.com/${user}/${repo}/workflows/${encodeURIComponent(
- workflow
- )}/badge.svg`,
- options: { qs: { branch } },
- valueMatcher: />([^<>]+)<\/tspan><\/text><\/g>
+ this.fetchPage({ page: ++i + 1, requestParams, schema }),
+ ),
+ )
+ return [...data].concat(...pageData)
+ }
+}
diff --git a/services/gitlab/gitlab-contributors-redirect.service.js b/services/gitlab/gitlab-contributors-redirect.service.js
new file mode 100644
index 0000000000000..b53240a26beba
--- /dev/null
+++ b/services/gitlab/gitlab-contributors-redirect.service.js
@@ -0,0 +1,13 @@
+import { retiredService } from '../index.js'
+
+// https://github.com/badges/shields/issues/8138
+export default retiredService({
+ category: 'build',
+ label: 'gitlab',
+ route: {
+ base: 'gitlab/v/contributor',
+ pattern: ':project+',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/gitlab/gitlab-contributors-redirect.tester.js b/services/gitlab/gitlab-contributors-redirect.tester.js
new file mode 100644
index 0000000000000..ddc26541ab95e
--- /dev/null
+++ b/services/gitlab/gitlab-contributors-redirect.tester.js
@@ -0,0 +1,7 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Contributors deprecated').get('/gitlab-org/gitlab.json').expectBadge({
+ label: 'gitlab',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/gitlab/gitlab-contributors.service.js b/services/gitlab/gitlab-contributors.service.js
new file mode 100644
index 0000000000000..d9fdc2d2cffa6
--- /dev/null
+++ b/services/gitlab/gitlab-contributors.service.js
@@ -0,0 +1,64 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { renderContributorBadge } from '../contributor-count.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const schema = Joi.object({ 'x-total': nonNegativeInteger }).required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabContributors extends GitLabBase {
+ static category = 'activity'
+ static route = {
+ base: 'gitlab/contributors',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/contributors/{project}': {
+ get: {
+ summary: 'GitLab Contributors',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'contributors' }
+
+ static render({ contributorCount }) {
+ return renderContributorBadge({ contributorCount })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ // https://docs.gitlab.com/ee/api/repositories.html#contributors
+ const { res } = await this._request(
+ this.authHelper.withBearerAuthHeader({
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/repository/contributors`,
+ options: { searchParams: { page: '1', per_page: '1' } },
+ httpErrors: httpErrorsFor('project not found'),
+ }),
+ )
+ const data = this.constructor._validate(res.headers, schema)
+ // The total number of contributors is in the `x-total` field in the headers.
+ // https://docs.gitlab.com/ee/api/index.html#other-pagination-headers
+ const contributorCount = data['x-total']
+ return this.constructor.render({ contributorCount })
+ }
+}
diff --git a/services/gitlab/gitlab-contributors.tester.js b/services/gitlab/gitlab-contributors.tester.js
new file mode 100644
index 0000000000000..1d93b88be6426
--- /dev/null
+++ b/services/gitlab/gitlab-contributors.tester.js
@@ -0,0 +1,42 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+import { noToken } from '../test-helpers.js'
+import _noGitLabToken from './gitlab-contributors.service.js'
+export const t = await createServiceTester()
+const noGitLabToken = noToken(_noGitLabToken)
+
+t.create('Contributors')
+ .get('/guoxudong.io/shields-test/licenced-test.json')
+ .expectBadge({
+ label: 'contributors',
+ message: isMetric,
+ })
+
+t.create('Contributors (repo not found)')
+ .get('/guoxudong.io/shields-test/do-not-exist.json')
+ .expectBadge({
+ label: 'contributors',
+ message: 'project not found',
+ })
+
+t.create('Mocking the missing x-total header')
+ .get('/group/project.json')
+ .intercept(nock =>
+ nock('https://gitlab.com')
+ .get(
+ '/api/v4/projects/group%2Fproject/repository/contributors?page=1&per_page=1',
+ )
+ .reply(200),
+ )
+ .expectBadge({
+ label: 'contributors',
+ message: 'invalid response data',
+ })
+
+t.create('Contributors (private repo)')
+ .skipWhen(noGitLabToken)
+ .get('/shields-ops-group/test.json')
+ .expectBadge({
+ label: 'contributors',
+ message: isMetric,
+ })
diff --git a/services/gitlab/gitlab-coverage-redirect.service.js b/services/gitlab/gitlab-coverage-redirect.service.js
new file mode 100644
index 0000000000000..15633e37db2fe
--- /dev/null
+++ b/services/gitlab/gitlab-coverage-redirect.service.js
@@ -0,0 +1,12 @@
+import { retiredService } from '../index.js'
+
+export default retiredService({
+ category: 'coverage',
+ label: 'gitlab',
+ route: {
+ base: 'gitlab/coverage',
+ pattern: ':user/:repo/:branch',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/gitlab/gitlab-coverage-redirect.tester.js b/services/gitlab/gitlab-coverage-redirect.tester.js
new file mode 100644
index 0000000000000..69be7ec5a0c2e
--- /dev/null
+++ b/services/gitlab/gitlab-coverage-redirect.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Coverage deprecated (with branch)')
+ .get('/gitlab-org/gitlab-runner/master.json')
+ .expectBadge({
+ label: 'gitlab',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('Coverage deprecated (with branch and job_name)')
+ .get('/gitlab-org/gitlab-runner/master.json?job_name=test coverage report')
+ .expectBadge({
+ label: 'gitlab',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('Coverage deprecated (with branch and gitlab_url)')
+ .get(
+ '/gitlab-org/gitlab-runner/master.json?gitlab_url=https://gitlab.gnome.org',
+ )
+ .expectBadge({
+ label: 'gitlab',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/gitlab/gitlab-forks.service.js b/services/gitlab/gitlab-forks.service.js
new file mode 100644
index 0000000000000..fcfe0f1e42bc1
--- /dev/null
+++ b/services/gitlab/gitlab-forks.service.js
@@ -0,0 +1,76 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import GitLabBase from './gitlab-base.js'
+import { description } from './gitlab-helper.js'
+
+const schema = Joi.object({
+ forks_count: nonNegativeInteger,
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabForks extends GitLabBase {
+ static category = 'social'
+
+ static route = {
+ base: 'gitlab/forks',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/forks/{project}': {
+ get: {
+ summary: 'GitLab Forks',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'forks', namedLogo: 'gitlab' }
+
+ static render({ baseUrl, project, forkCount }) {
+ return {
+ message: metric(forkCount),
+ style: 'social',
+ color: 'blue',
+ link: [
+ `${baseUrl}/${project}/-/forks/new`,
+ `${baseUrl}/${project}/-/forks`,
+ ],
+ }
+ }
+
+ async fetch({ project, baseUrl }) {
+ // https://docs.gitlab.com/ee/api/projects.html#get-single-project
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}`,
+ httpErrors: {
+ 404: 'project not found',
+ },
+ })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ const { forks_count: forkCount } = await this.fetch({
+ project,
+ baseUrl,
+ })
+ return this.constructor.render({ baseUrl, project, forkCount })
+ }
+}
diff --git a/services/gitlab/gitlab-forks.tester.js b/services/gitlab/gitlab-forks.tester.js
new file mode 100644
index 0000000000000..ca30f03b2c280
--- /dev/null
+++ b/services/gitlab/gitlab-forks.tester.js
@@ -0,0 +1,35 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Forks')
+ .get('/gitlab-org/gitlab.json')
+ .expectBadge({
+ label: 'forks',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://gitlab.com/gitlab-org/gitlab/-/forks/new',
+ 'https://gitlab.com/gitlab-org/gitlab/-/forks',
+ ],
+ })
+
+t.create('Forks (self-managed)')
+ .get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
+ .expectBadge({
+ label: 'forks',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://jihulab.com/gitlab-cn/gitlab/-/forks/new',
+ 'https://jihulab.com/gitlab-cn/gitlab/-/forks',
+ ],
+ })
+
+t.create('Forks (project not found)')
+ .get('/user1/gitlab-does-not-have-this-repo.json')
+ .expectBadge({
+ label: 'forks',
+ message: 'project not found',
+ })
diff --git a/services/gitlab/gitlab-go-mod.service.js b/services/gitlab/gitlab-go-mod.service.js
new file mode 100644
index 0000000000000..eeb9d310ca431
--- /dev/null
+++ b/services/gitlab/gitlab-go-mod.service.js
@@ -0,0 +1,137 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const queryParamSchema = Joi.object({
+ filename: Joi.string(),
+ gitlabUrl: Joi.string(),
+}).required()
+
+const goVersionRegExp = /^go ([^/\s]+)(\s*\/.+)?$/m
+
+const filenameDescription =
+ 'The `filename` param can be used to specify the path to `go.mod`. By default, we look for `go.mod` in the repo root'
+
+export default class GitlabGoModGoVersion extends GitLabBase {
+ static category = 'platform-support'
+ static route = {
+ base: 'gitlab/go-mod/go-version',
+ pattern: ':user/:repo/:branch*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/go-mod/go-version/{user}/{repo}': {
+ get: {
+ summary: 'GitLab go.mod Go version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitlab-org',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'gitlab-runner',
+ }),
+ queryParam({
+ name: 'filename',
+ example: 'src/go.mod',
+ description: filenameDescription,
+ }),
+ queryParam({
+ name: 'gitlabUrl',
+ example: 'https://gitlab.example.com',
+ }),
+ ],
+ },
+ },
+ '/gitlab/go-mod/go-version/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'GitLab go.mod Go version (branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'gitlab-org',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'gitlab-runner',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'main',
+ }),
+ queryParam({
+ name: 'filename',
+ example: 'src/go.mod',
+ description: filenameDescription,
+ }),
+ queryParam({
+ name: 'gitlabUrl',
+ example: 'https://gitlab.example.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'Go' }
+
+ static render({ version, branch }) {
+ return renderVersionBadge({
+ version,
+ tag: branch,
+ defaultLabel: 'Go',
+ })
+ }
+
+ async fetch({ user, repo, branch, filename, gitlabUrl }) {
+ const project = `${user}/${repo}`
+ // https://docs.gitlab.com/ee/api/repository_files.html#get-raw-file-from-repository
+ const url = `${gitlabUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/repository/files/${encodeURIComponent(filename)}/raw`
+ const options = { searchParams: { ref: branch || 'HEAD' } }
+ const httpErrors = httpErrorsFor('project or file not found')
+ const { buffer } = await this._request(
+ this.authHelper.withBearerAuthHeader({
+ url,
+ options,
+ httpErrors,
+ }),
+ )
+ return buffer
+ }
+
+ static transform(content) {
+ const match = goVersionRegExp.exec(content)
+ if (!match) {
+ throw new InvalidResponse({
+ prettyMessage: 'Go version missing in go.mod',
+ })
+ }
+
+ return {
+ go: match[1],
+ }
+ }
+
+ async handle(
+ { user, repo, branch },
+ { filename = 'go.mod', gitlabUrl = 'https://gitlab.com' },
+ ) {
+ const content = await this.fetch({
+ user,
+ repo,
+ branch,
+ filename,
+ gitlabUrl,
+ })
+ const { go } = this.constructor.transform(content.toString())
+ return this.constructor.render({ version: go, branch })
+ }
+}
diff --git a/services/gitlab/gitlab-go-mod.tester.js b/services/gitlab/gitlab-go-mod.tester.js
new file mode 100644
index 0000000000000..97891ac249d68
--- /dev/null
+++ b/services/gitlab/gitlab-go-mod.tester.js
@@ -0,0 +1,29 @@
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+const user = 'gitlab-org'
+const repo = 'gitlab-runner'
+
+t.create('go version in root')
+ .get(`/${user}/${repo}.json`)
+ .expectBadge({ label: 'Go', message: isVPlusDottedVersionAtLeastOne })
+
+t.create('go version in root (branch)')
+ .get(`/${user}/${repo}/main.json`)
+ .expectBadge({ label: 'Go@main', message: isVPlusDottedVersionAtLeastOne })
+
+t.create('project not found')
+ .get('/some-project/that-doesnt-exist.json')
+ .expectBadge({ label: 'Go', message: 'project or file not found' })
+
+t.create('go version in subdirectory')
+ .get(`/${user}/${repo}/main.json?filename=helpers/runner_wrapper/api/go.mod`)
+ .expectBadge({
+ label: 'Go@main',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
+t.create('file not found')
+ .get(`/${user}/${repo}/main.json?filename=nonexistent/go.mod`)
+ .expectBadge({ label: 'Go', message: 'project or file not found' })
diff --git a/services/gitlab/gitlab-helper.js b/services/gitlab/gitlab-helper.js
new file mode 100644
index 0000000000000..389ecc4cbdf84
--- /dev/null
+++ b/services/gitlab/gitlab-helper.js
@@ -0,0 +1,17 @@
+const description = `
+You may use your GitLab Project Id (e.g. 278964) or your Project Path (e.g.
+[gitlab-org/gitlab](https://gitlab.com/gitlab-org/gitlab) ).
+Note that only internet-accessible GitLab instances are supported, for example
+[https://jihulab.com](https://jihulab.com),
+[https://gitlab.gnome.org](https://gitlab.gnome.org), or
+[https://gitlab.com](https://gitlab.com).
+`
+
+function httpErrorsFor(notFoundMessage = 'project not found') {
+ return {
+ 401: notFoundMessage,
+ 404: notFoundMessage,
+ }
+}
+
+export { description, httpErrorsFor }
diff --git a/services/gitlab/gitlab-issues.service.js b/services/gitlab/gitlab-issues.service.js
new file mode 100644
index 0000000000000..f5373bc0cd805
--- /dev/null
+++ b/services/gitlab/gitlab-issues.service.js
@@ -0,0 +1,134 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const schema = Joi.object({
+ statistics: Joi.object({
+ counts: Joi.object({
+ all: nonNegativeInteger,
+ closed: nonNegativeInteger,
+ opened: nonNegativeInteger,
+ }).required(),
+ }),
+}).required()
+
+const queryParamSchema = Joi.object({
+ labels: Joi.string(),
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabIssues extends GitLabBase {
+ static category = 'issue-tracking'
+
+ static route = {
+ base: 'gitlab/issues',
+ pattern: ':variant(all|all-raw|open|open-raw|closed|closed-raw)/:project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/issues/{variant}/{project}': {
+ get: {
+ summary: 'GitLab Issues',
+ description,
+ parameters: [
+ pathParam({
+ name: 'variant',
+ example: 'all',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ }),
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'labels',
+ example: 'test,failure::new',
+ description:
+ 'If you want to use multiple labels, you can use a comma (,) to separate them, e.g. foo,bar',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'issues', color: 'informational' }
+
+ static render({ variant, raw, labels, issueCount }) {
+ const state = variant
+ const isMultiLabel = labels && labels.includes(',')
+ const labelText = labels ? `${isMultiLabel ? `${labels}` : labels} ` : ''
+
+ let labelPrefix = ''
+ let messageSuffix = ''
+ if (raw) {
+ labelPrefix = `${state} `
+ } else {
+ messageSuffix = state
+ }
+ return {
+ label: `${labelPrefix}${labelText}issues`,
+ message: `${metric(issueCount)}${
+ messageSuffix ? ' ' : ''
+ }${messageSuffix}`,
+ color: issueCount > 0 ? 'yellow' : 'brightgreen',
+ }
+ }
+
+ async fetch({ project, baseUrl, labels }) {
+ // https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/issues_statistics`,
+ options: labels ? { searchParams: { labels } } : undefined,
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ static transform({ variant, statistics }) {
+ const state = variant
+ let issueCount
+ switch (state) {
+ case 'open':
+ case 'open-raw':
+ issueCount = statistics.counts.opened
+ break
+ case 'closed':
+ case 'closed-raw':
+ issueCount = statistics.counts.closed
+ break
+ case 'all':
+ case 'all-raw':
+ issueCount = statistics.counts.all
+ break
+ }
+
+ return issueCount
+ }
+
+ async handle(
+ { variant, project },
+ { gitlab_url: baseUrl = 'https://gitlab.com', labels },
+ ) {
+ const { statistics } = await this.fetch({
+ project,
+ baseUrl,
+ labels,
+ })
+ return this.constructor.render({
+ variant: variant.split('-')[0],
+ raw: variant.endsWith('-raw'),
+ labels,
+ issueCount: this.constructor.transform({ variant, statistics }),
+ })
+ }
+}
diff --git a/services/gitlab/gitlab-issues.tester.js b/services/gitlab/gitlab-issues.tester.js
new file mode 100644
index 0000000000000..4a5c4e8e5bc74
--- /dev/null
+++ b/services/gitlab/gitlab-issues.tester.js
@@ -0,0 +1,148 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricOpenIssues,
+ isMetricClosedIssues,
+} from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Issues (project not found)')
+ .get('/open/guoxudong.io/shields-test/do-not-exist.json')
+ .expectBadge({
+ label: 'issues',
+ message: 'project not found',
+ })
+
+/**
+ * Opened issue number case
+ */
+t.create('Opened issues')
+ .get('/open/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues raw')
+ .get('/open-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'open issues',
+ message: isMetric,
+ })
+
+t.create('Open issues by label is > zero')
+ .get('/open/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'discussion issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues by multi-word label is > zero')
+ .get(
+ '/open/guoxudong.io/shields-test/issue-test.json?labels=discussion,enhancement',
+ )
+ .expectBadge({
+ label: 'discussion,enhancement issues',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open issues by label (raw)')
+ .get('/open-raw/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'open discussion issues',
+ message: isMetric,
+ })
+
+t.create('Opened issues by Scoped labels')
+ .timeout(10000)
+ .get('/open/gitlab-org%2Fgitlab.json?labels=test,failure::new')
+ .expectBadge({
+ label: 'test,failure::new issues',
+ message: isMetricOpenIssues,
+ })
+
+/**
+ * Closed issue number case
+ */
+t.create('Closed issues')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues raw')
+ .get('/closed-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'closed issues',
+ message: isMetric,
+ })
+
+t.create('Closed issues by label is > zero')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json?labels=bug')
+ .expectBadge({
+ label: 'bug issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues by multi-word label is > zero')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json?labels=bug,critical')
+ .expectBadge({
+ label: 'bug,critical issues',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed issues by label (raw)')
+ .get('/closed-raw/guoxudong.io/shields-test/issue-test.json?labels=bug')
+ .expectBadge({
+ label: 'closed bug issues',
+ message: isMetric,
+ })
+
+/**
+ * All issue number case
+ */
+t.create('All issues')
+ .get('/all/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'issues',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All issues raw')
+ .get('/all-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'all issues',
+ message: isMetric,
+ })
+
+t.create('All issues by label is > zero')
+ .get('/all/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'discussion issues',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All issues by multi-word label is > zero')
+ .get(
+ '/all/guoxudong.io/shields-test/issue-test.json?labels=discussion,enhancement',
+ )
+ .expectBadge({
+ label: 'discussion,enhancement issues',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All issues by label (raw)')
+ .get('/all-raw/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'all discussion issues',
+ message: isMetric,
+ })
diff --git a/services/gitlab/gitlab-languages-count.service.js b/services/gitlab/gitlab-languages-count.service.js
new file mode 100644
index 0000000000000..faeb33995b047
--- /dev/null
+++ b/services/gitlab/gitlab-languages-count.service.js
@@ -0,0 +1,73 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+/*
+We're expecting a response like { "Ruby": 67.13, "JavaScript": 19.66 }
+The keys could be anything and {} is a valid response (e.g: for an empty project)
+*/
+const schema = Joi.object().pattern(/./, Joi.number().min(0).max(100))
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabLanguageCount extends GitLabBase {
+ static category = 'analysis'
+
+ static route = {
+ base: 'gitlab/languages/count',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/languages/count/{project}': {
+ get: {
+ summary: 'GitLab Language Count',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'languages' }
+
+ static render({ languagesCount }) {
+ return {
+ message: metric(languagesCount),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ project, baseUrl }) {
+ // https://docs.gitlab.com/ee/api/projects.html#languages
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/languages`,
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ const data = await this.fetch({
+ project,
+ baseUrl,
+ })
+ return this.constructor.render({ languagesCount: Object.keys(data).length })
+ }
+}
diff --git a/services/gitlab/gitlab-languages-count.tester.js b/services/gitlab/gitlab-languages-count.tester.js
new file mode 100644
index 0000000000000..13a7b0ba054d2
--- /dev/null
+++ b/services/gitlab/gitlab-languages-count.tester.js
@@ -0,0 +1,25 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('language count').get('/gitlab-org/gitlab.json').expectBadge({
+ label: 'languages',
+ message: isMetric,
+ color: 'blue',
+})
+
+t.create('language count (self-managed)')
+ .get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
+ .expectBadge({
+ label: 'languages',
+ message: isMetric,
+ color: 'blue',
+ })
+
+t.create('language count (project not found)')
+ .get('/open/guoxudong.io/shields-test/do-not-exist.json')
+ .expectBadge({
+ label: 'languages',
+ message: 'project not found',
+ })
diff --git a/services/gitlab/gitlab-last-commit.service.js b/services/gitlab/gitlab-last-commit.service.js
new file mode 100644
index 0000000000000..3f44197bfd4e2
--- /dev/null
+++ b/services/gitlab/gitlab-last-commit.service.js
@@ -0,0 +1,91 @@
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { optionalUrl, relativeUri } from '../validators.js'
+import GitLabBase from './gitlab-base.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+
+const schema = Joi.array()
+ .items(
+ Joi.object({
+ committed_date: Joi.string().required(),
+ }),
+ )
+ .required()
+
+const queryParamSchema = Joi.object({
+ ref: Joi.string(),
+ gitlab_url: optionalUrl,
+ path: relativeUri,
+}).required()
+
+const refText = `
+ref can be filled with the name of a branch, tag or revision range of the repository.
+`
+
+const lastCommitDescription = description + refText
+
+export default class GitlabLastCommit extends GitLabBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'gitlab/last-commit',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/last-commit/{project}': {
+ get: {
+ summary: 'GitLab Last Commit',
+ description: lastCommitDescription,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'ref',
+ example: 'master',
+ }),
+ queryParam({
+ name: 'path',
+ example: 'README.md',
+ schema: { type: 'string' },
+ description: 'File path to resolve the last commit for.',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last commit' }
+
+ async fetch({ project, baseUrl, ref, path }) {
+ // https://docs.gitlab.com/ee/api/commits.html#list-repository-commits
+ return super.fetch({
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/repository/commits`,
+ options: { searchParams: { ref_name: ref, path, per_page: 1 } },
+ schema,
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ async handle(
+ { project },
+ { gitlab_url: baseUrl = 'https://gitlab.com', ref, path },
+ ) {
+ const data = await this.fetch({ project, baseUrl, ref, path })
+ const [commit] = data
+
+ if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
+
+ return renderDateBadge(commit.committed_date)
+ }
+}
diff --git a/services/gitlab/gitlab-last-commit.tester.js b/services/gitlab/gitlab-last-commit.tester.js
new file mode 100644
index 0000000000000..870004cd706d4
--- /dev/null
+++ b/services/gitlab/gitlab-last-commit.tester.js
@@ -0,0 +1,68 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('last commit (recent)').get('/gitlab-org/gitlab.json').expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+})
+
+t.create('last commit (on ref) (ancient)')
+ .get('/gitlab-org/gitlab.json?ref=v13.8.6-ee')
+ .expectBadge({
+ label: 'last commit',
+ message: 'march 2021',
+ })
+
+t.create('last commit (on ref) (ancient) (by top-level file path)')
+ .get('/gitlab-org/gitlab.json?ref=v13.8.6-ee&path=README.md')
+ .expectBadge({
+ label: 'last commit',
+ message: 'december 2020',
+ })
+
+t.create('last commit (on ref) (ancient) (by top-level dir path)')
+ .get('/gitlab-org/gitlab.json?ref=v13.8.6-ee&path=changelogs')
+ .expectBadge({
+ label: 'last commit',
+ message: 'march 2021',
+ })
+
+t.create(
+ 'last commit (on ref) (ancient) (by top-level dir path with trailing slash)',
+)
+ .get('/gitlab-org/gitlab.json?ref=v13.8.6-ee&path=changelogs/')
+ .expectBadge({
+ label: 'last commit',
+ message: 'march 2021',
+ })
+
+t.create('last commit (on ref) (ancient) (by nested file path)')
+ .get('/gitlab-org/gitlab.json?ref=v13.8.6-ee&path=changelogs/README.md')
+ .expectBadge({
+ label: 'last commit',
+ message: 'september 2020',
+ })
+
+t.create('last commit (self-managed)')
+ .get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
+ .expectBadge({
+ label: 'last commit',
+ message: isFormattedDate,
+ })
+
+t.create('last commit (project not found)')
+ .get('/open/guoxudong.io/shields-test/do-not-exist.json')
+ .expectBadge({
+ label: 'last commit',
+ message: 'project not found',
+ })
+
+t.create('last commit (no commits found)')
+ .timeout(10000)
+ .get('/gitlab-org/gitlab.json?path=not/a/dir')
+ .expectBadge({
+ label: 'last commit',
+ message: 'no commits found',
+ })
diff --git a/services/gitlab/gitlab-license-redirect.service.js b/services/gitlab/gitlab-license-redirect.service.js
new file mode 100644
index 0000000000000..9513d6ae2bccd
--- /dev/null
+++ b/services/gitlab/gitlab-license-redirect.service.js
@@ -0,0 +1,13 @@
+import { retiredService } from '../index.js'
+
+// https://github.com/badges/shields/issues/8138
+export default retiredService({
+ category: 'build',
+ label: 'gitlab',
+ route: {
+ base: 'gitlab/v/license',
+ pattern: ':project+',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/gitlab/gitlab-license-redirect.tester.js b/services/gitlab/gitlab-license-redirect.tester.js
new file mode 100644
index 0000000000000..5c957a52cc463
--- /dev/null
+++ b/services/gitlab/gitlab-license-redirect.tester.js
@@ -0,0 +1,7 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('License deprecated').get('/gitlab-org/gitlab.json').expectBadge({
+ label: 'gitlab',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/gitlab/gitlab-license.service.js b/services/gitlab/gitlab-license.service.js
new file mode 100644
index 0000000000000..54aa99fb980c1
--- /dev/null
+++ b/services/gitlab/gitlab-license.service.js
@@ -0,0 +1,74 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl } from '../validators.js'
+import { renderLicenseBadge } from '../licenses.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const schema = Joi.object({
+ license: Joi.object({
+ name: Joi.string().required(),
+ }).allow(null),
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabLicense extends GitLabBase {
+ static category = 'license'
+
+ static route = {
+ base: 'gitlab/license',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/license/{project}': {
+ get: {
+ summary: 'GitLab License',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'license' }
+
+ static render({ license }) {
+ if (license) {
+ return renderLicenseBadge({ license })
+ } else {
+ return { message: 'not specified' }
+ }
+ }
+
+ async fetch({ project, baseUrl }) {
+ // https://docs.gitlab.com/ee/api/projects.html#get-single-project
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}`,
+ options: { searchParams: { license: '1' } },
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ const { license: licenseObject } = await this.fetch({
+ project,
+ baseUrl,
+ })
+ const license = licenseObject ? licenseObject.name : undefined
+ return this.constructor.render({ license })
+ }
+}
diff --git a/services/gitlab/gitlab-license.tester.js b/services/gitlab/gitlab-license.tester.js
new file mode 100644
index 0000000000000..8ddfaf25af37a
--- /dev/null
+++ b/services/gitlab/gitlab-license.tester.js
@@ -0,0 +1,80 @@
+import { licenseToColor } from '../licenses.js'
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import _noGitLabToken from './gitlab-license.service.js'
+export const t = await createServiceTester()
+const noGitLabToken = noToken(_noGitLabToken)
+
+const publicDomainLicenseColor = licenseToColor('MIT License')
+const unknownLicenseColor = licenseToColor()
+
+t.create('License')
+ .get('/guoxudong.io/shields-test/licenced-test.json')
+ .expectBadge({
+ label: 'license',
+ message: 'MIT License',
+ color: `${publicDomainLicenseColor}`,
+ })
+
+t.create('License for repo without a license')
+ .get('/guoxudong.io/shields-test/no-license-test.json')
+ .expectBadge({
+ label: 'license',
+ message: 'not specified',
+ color: 'lightgrey',
+ })
+
+t.create('Other license')
+ .get('/group/project.json')
+ .intercept(nock =>
+ nock('https://gitlab.com')
+ .get('/api/v4/projects/group%2Fproject?license=1')
+ .reply(200, {
+ license: {
+ name: 'Other',
+ },
+ }),
+ )
+ .expectBadge({
+ label: 'license',
+ message: 'Other',
+ color: unknownLicenseColor,
+ })
+
+t.create('License for unknown repo')
+ .get('/user1/gitlab-does-not-have-this-repo.json')
+ .expectBadge({
+ label: 'license',
+ message: 'project not found',
+ color: 'red',
+ })
+
+t.create('Mocking License')
+ .get('/group/project.json')
+ .intercept(nock =>
+ nock('https://gitlab.com')
+ .get('/api/v4/projects/group%2Fproject?license=1')
+ .reply(200, {
+ license: {
+ key: 'apache-2.0',
+ name: 'Apache License 2.0',
+ nickname: '',
+ html_url: 'http://choosealicense.com/licenses/apache-2.0/',
+ source_url: '',
+ },
+ }),
+ )
+ .expectBadge({
+ label: 'license',
+ message: 'Apache License 2.0',
+ color: unknownLicenseColor,
+ })
+
+t.create('License (private repo)')
+ .skipWhen(noGitLabToken)
+ .get('/shields-ops-group/test.json')
+ .expectBadge({
+ label: 'license',
+ message: 'MIT License',
+ color: `${publicDomainLicenseColor}`,
+ })
diff --git a/services/gitlab/gitlab-merge-requests.service.js b/services/gitlab/gitlab-merge-requests.service.js
new file mode 100644
index 0000000000000..861c878ce8b20
--- /dev/null
+++ b/services/gitlab/gitlab-merge-requests.service.js
@@ -0,0 +1,140 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+// The total number of MR is in the `x-total` field in the headers.
+// https://docs.gitlab.com/ee/api/index.html#other-pagination-headers
+const schema = Joi.object({
+ 'x-total': Joi.number().integer(),
+ 'x-page': nonNegativeInteger,
+})
+
+const queryParamSchema = Joi.object({
+ labels: Joi.string(),
+ gitlab_url: optionalUrl,
+}).required()
+
+const more = `
+GitLab's API only reports up to 10k Merge Requests, so badges for projects that have more than 10k will not have an exact count.
+`
+
+const mergeRequestsDescription = description + more
+
+export default class GitlabMergeRequests extends GitLabBase {
+ static category = 'issue-tracking'
+
+ static route = {
+ base: 'gitlab/merge-requests',
+ pattern:
+ ':variant(all|all-raw|open|open-raw|closed|closed-raw|locked|locked-raw|merged|merged-raw)/:project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/merge-requests/{variant}/{project}': {
+ get: {
+ summary: 'GitLab Merge Requests',
+ description: mergeRequestsDescription,
+ parameters: [
+ pathParam({
+ name: 'variant',
+ example: 'all',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ }),
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'labels',
+ example: 'test,type::feature',
+ description:
+ 'If you want to use multiple labels, you can use a comma (,) to separate them, e.g. foo,bar',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'merge requests' }
+
+ static render({ variant, raw, labels, mergeRequestCount }) {
+ const state = variant
+ const isMultiLabel = labels && labels.includes(',')
+ const labelText = labels ? `${isMultiLabel ? `${labels}` : labels} ` : ''
+
+ let labelPrefix = ''
+ let messageSuffix = ''
+ if (raw) {
+ labelPrefix = `${state} `
+ } else {
+ messageSuffix = state
+ }
+ const message = `${mergeRequestCount > 10000 ? 'more than ' : ''}${metric(
+ mergeRequestCount,
+ )}${messageSuffix ? ' ' : ''}${messageSuffix}`
+ return {
+ label: `${labelPrefix}${labelText}merge requests`,
+ message,
+ color: 'blue',
+ }
+ }
+
+ async fetch({ project, baseUrl, variant, labels }) {
+ // https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests
+ const { res } = await this._request(
+ this.authHelper.withBearerAuthHeader({
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/merge_requests`,
+ options: {
+ searchParams: {
+ state: variant === 'open' ? 'opened' : variant,
+ page: '1',
+ per_page: '1',
+ labels,
+ },
+ },
+ httpErrors: httpErrorsFor('project not found'),
+ }),
+ )
+ return this.constructor._validate(res.headers, schema)
+ }
+
+ static transform(data) {
+ if (data['x-total'] !== undefined) {
+ return data['x-total']
+ } else {
+ // https://docs.gitlab.com/ee/api/index.html#pagination-response-headers
+ // For performance reasons, if a query returns more than 10,000 records, GitLab doesn’t return `x-total` header.
+ // Displayed on the page as "more than 10k".
+ return 10001
+ }
+ }
+
+ async handle(
+ { variant, project },
+ { gitlab_url: baseUrl = 'https://gitlab.com', labels },
+ ) {
+ const data = await this.fetch({
+ project,
+ baseUrl,
+ variant: variant.split('-')[0],
+ labels,
+ })
+ const mergeRequestCount = this.constructor.transform(data)
+ return this.constructor.render({
+ variant: variant.split('-')[0],
+ raw: variant.endsWith('-raw'),
+ labels,
+ mergeRequestCount,
+ })
+ }
+}
diff --git a/services/gitlab/gitlab-merge-requests.spec.js b/services/gitlab/gitlab-merge-requests.spec.js
new file mode 100644
index 0000000000000..cb809ed17fc3b
--- /dev/null
+++ b/services/gitlab/gitlab-merge-requests.spec.js
@@ -0,0 +1,92 @@
+import { test, given } from 'sazerac'
+import nock from 'nock'
+import { expect } from 'chai'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import GitlabMergeRequests from './gitlab-merge-requests.service.js'
+
+describe('GitlabMergeRequests', function () {
+ test(GitlabMergeRequests.render, () => {
+ given({ variant: 'open', mergeRequestCount: 1399 }).expect({
+ label: 'merge requests',
+ message: '1.4k open',
+ color: 'blue',
+ })
+ given({ variant: 'open', raw: '-raw', mergeRequestCount: 1399 }).expect({
+ label: 'open merge requests',
+ message: '1.4k',
+ color: 'blue',
+ })
+ given({
+ variant: 'open',
+ labels: 'discussion,enhancement',
+ mergeRequestCount: 15,
+ }).expect({
+ label: 'discussion,enhancement merge requests',
+ message: '15 open',
+ color: 'blue',
+ })
+ given({
+ variant: 'open',
+ raw: '-raw',
+ labels: 'discussion,enhancement',
+ mergeRequestCount: 15,
+ }).expect({
+ label: 'open discussion,enhancement merge requests',
+ message: '15',
+ color: 'blue',
+ })
+ given({ variant: 'open', mergeRequestCount: 0 }).expect({
+ label: 'merge requests',
+ message: '0 open',
+ color: 'blue',
+ })
+ given({ variant: 'open', mergeRequestCount: 10001 }).expect({
+ label: 'merge requests',
+ message: 'more than 10k open',
+ color: 'blue',
+ })
+ })
+ describe('auth', function () {
+ cleanUpNockAfterEach()
+
+ const fakeToken = 'abc123'
+ const config = {
+ public: {
+ services: {
+ gitlab: {
+ authorizedOrigins: ['https://gitlab.com'],
+ },
+ },
+ },
+ private: {
+ gitlab_token: fakeToken,
+ },
+ }
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://gitlab.com/')
+ .get(
+ '/api/v4/projects/foo%2Fbar/merge_requests?state=opened&page=1&per_page=1',
+ )
+ // This ensures that the expected credentials are actually being sent with the HTTP request.
+ // Without this the request wouldn't match and the test would fail.
+ .matchHeader('Authorization', `Bearer ${fakeToken}`)
+ .reply(200, {}, { 'x-total': '100', 'x-page': '1' })
+
+ expect(
+ await GitlabMergeRequests.invoke(
+ defaultContext,
+ config,
+ { project: 'foo/bar', variant: 'open' },
+ {},
+ ),
+ ).to.deep.equal({
+ label: 'merge requests',
+ message: '100 open',
+ color: 'blue',
+ })
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/gitlab/gitlab-merge-requests.tester.js b/services/gitlab/gitlab-merge-requests.tester.js
new file mode 100644
index 0000000000000..2a72e501d622f
--- /dev/null
+++ b/services/gitlab/gitlab-merge-requests.tester.js
@@ -0,0 +1,172 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricOpenIssues,
+ isMetricClosedIssues,
+} from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Merge Requests (project not found)')
+ .get('/open/guoxudong.io/shields-test/do-not-exist.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: 'project not found',
+ })
+
+/**
+ * Opened issue number case
+ */
+t.create('Opened merge requests')
+ .get('/open/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open merge requests raw')
+ .get('/open-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'open merge requests',
+ message: isMetric,
+ })
+
+t.create('Open merge requests by label is > zero')
+ .get('/open/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'discussion merge requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open merge requests by multi-word label is > zero')
+ .get(
+ '/open/guoxudong.io/shields-test/issue-test.json?labels=discussion,enhancement',
+ )
+ .expectBadge({
+ label: 'discussion,enhancement merge requests',
+ message: isMetricOpenIssues,
+ })
+
+t.create('Open merge requests by label (raw)')
+ .get('/open-raw/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'open discussion merge requests',
+ message: isMetric,
+ })
+
+t.create('Opened merge requests by Scoped labels')
+ .get('/open/gitlab-org%2Fgitlab.json?labels=test,failure::new')
+ .expectBadge({
+ label: 'test,failure::new merge requests',
+ message: Joi.alternatives(isMetricOpenIssues, Joi.equal('0 open')),
+ })
+
+/**
+ * Closed issue number case
+ */
+t.create('Closed merge requests')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: isMetricClosedIssues,
+ })
+
+t.create('Closed merge requests raw')
+ .get('/closed-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'closed merge requests',
+ message: isMetric,
+ })
+
+t.create('Closed merge requests by label is > zero')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json?labels=bug')
+ .expectBadge({
+ label: 'bug merge requests',
+ message: Joi.alternatives(isMetricClosedIssues, Joi.equal('0 closed')),
+ })
+
+t.create('Closed merge requests by multi-word label is > zero')
+ .get('/closed/guoxudong.io/shields-test/issue-test.json?labels=bug,critical')
+ .expectBadge({
+ label: 'bug,critical merge requests',
+ message: Joi.alternatives(isMetricClosedIssues, Joi.equal('0 closed')),
+ })
+
+t.create('Closed merge requests by label (raw)')
+ .get(
+ '/closed-raw/guoxudong.io/shields-test/issue-test.json?labels=enhancement',
+ )
+ .expectBadge({
+ label: 'closed enhancement merge requests',
+ message: isMetric,
+ })
+
+/**
+ * All issue number case
+ */
+t.create('All merge requests')
+ .get('/all/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All merge requests raw')
+ .get('/all-raw/guoxudong.io/shields-test/issue-test.json')
+ .expectBadge({
+ label: 'all merge requests',
+ message: isMetric,
+ })
+
+t.create('All merge requests by label is > zero')
+ .get('/all/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'discussion merge requests',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All merge requests by multi-word label is > zero')
+ .get(
+ '/all/guoxudong.io/shields-test/issue-test.json?labels=discussion,enhancement',
+ )
+ .expectBadge({
+ label: 'discussion,enhancement merge requests',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) all$/,
+ ),
+ })
+
+t.create('All merge requests by label (raw)')
+ .get('/all-raw/guoxudong.io/shields-test/issue-test.json?labels=discussion')
+ .expectBadge({
+ label: 'all discussion merge requests',
+ message: isMetric,
+ })
+
+t.create('more than 10k merge requests')
+ .get('/all/gitlab-org%2Fgitlab.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: 'more than 10k all',
+ })
+
+t.create('locked merge requests')
+ .get('/locked/gitlab-org%2Fgitlab.json')
+ .expectBadge({
+ label: 'merge requests',
+ message: Joi.string().regex(
+ /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) locked$/,
+ ),
+ })
+
+t.create('Opened merge requests (self-managed)')
+ .get('/open/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
+ .expectBadge({
+ label: 'merge requests',
+ message: isMetricOpenIssues,
+ })
diff --git a/services/gitlab/gitlab-pipeline-coverage.service.js b/services/gitlab/gitlab-pipeline-coverage.service.js
new file mode 100644
index 0000000000000..62845a5c0fce1
--- /dev/null
+++ b/services/gitlab/gitlab-pipeline-coverage.service.js
@@ -0,0 +1,122 @@
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { optionalUrl } from '../validators.js'
+import {
+ BaseSvgScrapingService,
+ NotFound,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+
+const schema = Joi.object({
+ message: Joi.string()
+ .regex(/^([0-9]+\.[0-9]+%)|unknown$/)
+ .required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+ job_name: Joi.string(),
+ branch: Joi.string(),
+}).required()
+
+const moreDocs = `
+Important: If your project is publicly visible, but the badge is like this:
+
+
+Check if your pipelines are publicly visible as well.
+Navigate to your project settings on GitLab and choose General Pipelines under CI/CD.
+Then tick the setting Public pipelines.
+
+Now your settings should look like this:
+
+
+
+Also make sure you have set up code covrage parsing as described here
+
+Your badge should be working fine now.
+`
+
+export default class GitlabPipelineCoverage extends BaseSvgScrapingService {
+ static category = 'coverage'
+
+ static route = {
+ base: 'gitlab/pipeline-coverage',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/pipeline-coverage/{project}': {
+ get: {
+ summary: 'Gitlab Code Coverage',
+ description: description + moreDocs,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'job_name',
+ example: 'jest-integration',
+ }),
+ queryParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'coverage' }
+
+ static render({ coverage }) {
+ return {
+ message: `${coverage.toFixed(0)}%`,
+ color: coveragePercentage(coverage),
+ }
+ }
+
+ async fetch({ project, baseUrl = 'https://gitlab.com', jobName, branch }) {
+ // Since the URL doesn't return a usable value when an invalid job name is specified,
+ // it is recommended to not use the query param at all if not required
+ jobName = jobName ? `?job=${jobName}` : ''
+ const url = `${baseUrl}/${decodeURIComponent(
+ project,
+ )}/badges/${branch}/coverage.svg${jobName}`
+ const httpErrors = httpErrorsFor('project not found')
+ return this._requestSvg({
+ schema,
+ url,
+ httpErrors,
+ })
+ }
+
+ static transform({ coverage }) {
+ if (coverage === 'unknown') {
+ throw new NotFound({ prettyMessage: 'not set up' })
+ }
+ return Number(coverage.slice(0, -1))
+ }
+
+ async handle(
+ { project },
+ { gitlab_url: baseUrl, job_name: jobName, branch },
+ ) {
+ const { message: coverage } = await this.fetch({
+ project,
+ branch,
+ baseUrl,
+ jobName,
+ })
+ return this.constructor.render({
+ coverage: this.constructor.transform({ coverage }),
+ })
+ }
+}
diff --git a/services/gitlab/gitlab-pipeline-coverage.tester.js b/services/gitlab/gitlab-pipeline-coverage.tester.js
new file mode 100644
index 0000000000000..c0917a445f2fe
--- /dev/null
+++ b/services/gitlab/gitlab-pipeline-coverage.tester.js
@@ -0,0 +1,73 @@
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Coverage (branch)')
+ .get('/gitlab-org/gitlab-runner.json?branch=12-0-stable')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('Coverage (existent branch but coverage not set up)')
+ .get('/gitlab-org/gitlab-git-http-server.json?branch=master')
+ .expectBadge({
+ label: 'coverage',
+ message: 'not set up',
+ })
+
+t.create('Coverage (nonexistent branch)')
+ .get('/gitlab-org/gitlab-runner.json?branch=nope-not-a-branch')
+ .expectBadge({
+ label: 'coverage',
+ message: 'not set up',
+ })
+
+// Gitlab will redirect users to a sign-in page
+// (which we ultimately see as a 403 error) in the event
+// a nonexistent, or private, repository is specified.
+// Given the additional complexity that would've been required to
+// present users with a more traditional and friendly 'Not Found'
+// error message, we will simply display invalid
+// https://github.com/badges/shields/pull/5538
+// https://github.com/badges/shields/pull/9752
+t.create('Coverage (nonexistent repo)')
+ .get('/this-repo/does-not-exist.json')
+ .expectBadge({
+ label: 'coverage',
+ message: 'invalid',
+ })
+
+t.create('Coverage (custom job)')
+ .get(
+ '/gitlab-org/gitlab-runner.json?branch=12-0-stable&job_name=test coverage report',
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('Coverage (custom invalid job)')
+ .get(
+ '/gitlab-org/gitlab-runner.json?branch=12-0-stable&job_name=i dont exist',
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: 'not set up',
+ })
+
+t.create('Coverage (custom gitlab URL)')
+ .get('/sdk/kde-builder.json?gitlab_url=https://invent.kde.org&branch=master')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('Coverage (custom gitlab URL and job)')
+ .get(
+ '/sdk/kde-builder.json?gitlab_url=https://invent.kde.org&branch=master&job_name=unit_and_integration_tests',
+ )
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
diff --git a/services/gitlab/gitlab-pipeline-status.service.js b/services/gitlab/gitlab-pipeline-status.service.js
index 50e75e327ff06..30b5c484ec645 100644
--- a/services/gitlab/gitlab-pipeline-status.service.js
+++ b/services/gitlab/gitlab-pipeline-status.service.js
@@ -1,9 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { optionalUrl } = require('../validators')
-const { BaseSvgScrapingService, NotFound } = require('..')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { optionalUrl } from '../validators.js'
+import {
+ BaseSvgScrapingService,
+ NotFound,
+ redirector,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
const badgeSchema = Joi.object({
message: Joi.alternatives()
@@ -13,90 +18,120 @@ const badgeSchema = Joi.object({
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
+ branch: Joi.string(),
}).required()
-const documentation = `
-
- Important: If your project is publicly visible, but the badge is like this:
-
-
-
- Check if your pipelines are publicly visible as well.
- Navigate to your project settings on GitLab and choose General Pipelines under CI/CD.
- Then tick the setting Public pipelines.
-
-
- Now your settings should look like this:
-
+const moreDocs = `
+Important: You must use the Project Path, not the Project Id. Additionally, if your project is publicly visible, but the badge is like this:
+
+
+Check if your pipelines are publicly visible as well.
+Navigate to your project settings on GitLab and choose General Pipelines under CI/CD.
+Then tick the setting Public pipelines.
+
+Now your settings should look like this:
+
-
- Your badge should be working fine now.
-
+
+Your badge should be working fine now.
+
+NB - The badge will display 'inaccessible' if the specified repo was not found on the target Gitlab instance.
`
-module.exports = class GitlabPipelineStatus extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
+class GitlabPipelineStatus extends BaseSvgScrapingService {
+ static category = 'build'
- static get route() {
- return {
- base: 'gitlab/pipeline',
- pattern: ':user/:repo/:branch*',
- queryParamSchema,
- }
+ static route = {
+ base: 'gitlab/pipeline-status',
+ pattern: ':project+',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Gitlab pipeline status',
- pattern: ':user/:repo',
- namedParams: { user: 'gitlab-org', repo: 'gitlab-ce' },
- staticPreview: this.render({ status: 'passed' }),
- documentation,
- },
- {
- title: 'Gitlab pipeline status (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'gitlab-org',
- repo: 'gitlab-ce',
- branch: 'master',
- },
- staticPreview: this.render({ status: 'passed' }),
- documentation,
+ static openApi = {
+ '/gitlab/pipeline-status/{project}': {
+ get: {
+ summary: 'Gitlab Pipeline Status',
+ description: description + moreDocs,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'branch',
+ example: 'master',
+ }),
+ ],
},
- {
- title: 'Gitlab pipeline status (self-hosted)',
- pattern: ':user/:repo',
- namedParams: { user: 'GNOME', repo: 'pango' },
- queryParams: { gitlab_url: 'https://gitlab.gnome.org' },
- staticPreview: this.render({ status: 'passed' }),
- documentation,
- },
- ]
+ },
}
static render({ status }) {
return renderBuildStatusBadge({ status })
}
- async handle(
- { user, repo, branch = 'master' },
- { gitlab_url: baseUrl = 'https://gitlab.com' }
- ) {
- const { message: status } = await this._requestSvg({
+ async fetch({ project, branch, baseUrl }) {
+ return this._requestSvg({
schema: badgeSchema,
- url: `${baseUrl}/${user}/${repo}/badges/${branch}/pipeline.svg`,
- errorMessages: {
- 401: 'repo not found',
- 404: 'repo not found',
- },
+ url: `${baseUrl}/${decodeURIComponent(
+ project,
+ )}/badges/${branch}/pipeline.svg`,
+ httpErrors: httpErrorsFor('project not found'),
})
+ }
+
+ static transform(data) {
+ const { message: status } = data
if (status === 'unknown') {
throw new NotFound({ prettyMessage: 'branch not found' })
}
+ return { status }
+ }
+
+ async handle(
+ { project },
+ { gitlab_url: baseUrl = 'https://gitlab.com', branch = 'main' },
+ ) {
+ const data = await this.fetch({
+ project,
+ branch,
+ baseUrl,
+ })
+ const { status } = this.constructor.transform(data)
return this.constructor.render({ status })
}
}
+
+const GitlabPipelineStatusRedirector = redirector({
+ category: 'build',
+ name: 'GitlabPipelineStatusRedirector',
+ route: {
+ base: 'gitlab/pipeline',
+ pattern: ':user/:repo',
+ },
+ transformPath: ({ user, repo }) => `/gitlab/pipeline-status/${user}/${repo}`,
+ transformQueryParams: ({ _b }) => ({ branch: 'master' }),
+ dateAdded: new Date('2020-07-12'),
+})
+
+const GitlabPipelineStatusBranchRouteParamRedirector = redirector({
+ category: 'build',
+ name: 'GitlabPipelineStatusBranchRouteParamRedirector',
+ route: {
+ base: 'gitlab/pipeline',
+ pattern: ':user/:repo/:branch+',
+ },
+ transformPath: ({ user, repo }) => `/gitlab/pipeline-status/${user}/${repo}`,
+ transformQueryParams: ({ branch }) => ({ branch }),
+ dateAdded: new Date('2021-10-20'),
+})
+
+export {
+ GitlabPipelineStatus,
+ GitlabPipelineStatusRedirector,
+ GitlabPipelineStatusBranchRouteParamRedirector,
+}
diff --git a/services/gitlab/gitlab-pipeline-status.tester.js b/services/gitlab/gitlab-pipeline-status.tester.js
index 0d015d2d9286f..f4fd9d410d8c0 100644
--- a/services/gitlab/gitlab-pipeline-status.tester.js
+++ b/services/gitlab/gitlab-pipeline-status.tester.js
@@ -1,39 +1,64 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
+ id: 'GitlabPipeline',
+ title: 'Gitlab Pipeline',
+ pathPrefix: '/gitlab',
+})
t.create('Pipeline status')
- .get('/gitlab-org/gitlab-ce.json')
+ .get('/pipeline-status/gitlab-org/gitlab.json?branch=ruby-next')
.expectBadge({
label: 'build',
message: isBuildStatus,
})
-t.create('Pipeline status (branch)')
- .get('/gitlab-org/gitlab-ce/v10.7.6.json')
+t.create('Pipeline status (nested groups)')
+ .get(
+ '/pipeline-status/megabyte-labs/docker/ci-pipeline/ansible.json?branch=master',
+ )
.expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('Pipeline status (nonexistent branch)')
- .get('/gitlab-org/gitlab-ce/nope-not-a-branch.json')
+ .get('/pipeline-status/gitlab-org/gitlab.json?branch=nope-not-a-branch')
.expectBadge({
label: 'build',
message: 'branch not found',
})
+// Gitlab will redirect users to a sign-in page
+// (which we ultimately see as a 403 error) in the event
+// a nonexistent, or private, repository is specified.
+// Given the additional complexity that would've been required to
+// present users with a more traditional and friendly 'Not Found'
+// error message, we will simply display invalid
+// https://github.com/badges/shields/pull/5538
+// https://github.com/badges/shields/pull/9752
t.create('Pipeline status (nonexistent repo)')
- .get('/this-repo/does-not-exist.json')
+ .get('/pipeline-status/this-repo/does-not-exist.json?branch=master')
.expectBadge({
label: 'build',
- message: 'repo not found',
+ message: 'invalid',
})
t.create('Pipeline status (custom gitlab URL)')
- .get('/GNOME/pango.json?gitlab_url=https://gitlab.gnome.org')
+ .get(
+ '/pipeline-status/sdk/kde-builder.json?gitlab_url=https://invent.kde.org&branch=master',
+ )
.expectBadge({
label: 'build',
message: isBuildStatus,
})
+
+t.create('Pipeline no branch redirect')
+ .get('/pipeline/gitlab-org/gitlab.svg')
+ .expectRedirect('/gitlab/pipeline-status/gitlab-org/gitlab.svg?branch=master')
+
+t.create('Pipeline legacy route with branch redirect')
+ .get('/pipeline/gitlab-org/gitlab/v10.7.6?style=flat')
+ .expectRedirect(
+ '/gitlab/pipeline-status/gitlab-org/gitlab.svg?style=flat&branch=v10.7.6',
+ )
diff --git a/services/gitlab/gitlab-release.service.js b/services/gitlab/gitlab-release.service.js
new file mode 100644
index 0000000000000..81cac9ce19044
--- /dev/null
+++ b/services/gitlab/gitlab-release.service.js
@@ -0,0 +1,133 @@
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { latest, renderVersionBadge } from '../version.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const schema = Joi.array().items(
+ Joi.object({
+ name: Joi.string().required(),
+ tag_name: Joi.string().required(),
+ }),
+)
+
+const sortEnum = ['date', 'semver']
+const displayNameEnum = ['tag', 'release']
+const dateOrderByEnum = ['created_at', 'released_at']
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+ include_prereleases: Joi.equal(''),
+ sort: Joi.string()
+ .valid(...sortEnum)
+ .default('date'),
+ display_name: Joi.string()
+ .valid(...displayNameEnum)
+ .default('tag'),
+ date_order_by: Joi.string()
+ .valid(...dateOrderByEnum)
+ .default('created_at'),
+}).required()
+
+export default class GitLabRelease extends GitLabBase {
+ static category = 'version'
+
+ static route = {
+ base: 'gitlab/v/release',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/v/release/{project}': {
+ get: {
+ summary: 'GitLab Release',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ queryParam({
+ name: 'sort',
+ schema: { type: 'string', enum: sortEnum },
+ example: 'semver',
+ }),
+ queryParam({
+ name: 'display_name',
+ schema: { type: 'string', enum: displayNameEnum },
+ example: 'release',
+ }),
+ queryParam({
+ name: 'date_order_by',
+ schema: { type: 'string', enum: dateOrderByEnum },
+ example: 'created_at',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'release' }
+
+ async fetch({ project, baseUrl, isSemver, orderBy }) {
+ // https://docs.gitlab.com/ee/api/releases/
+ return this.fetchPaginatedArrayData({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}/releases`,
+ httpErrors: httpErrorsFor('project not found'),
+ options: {
+ searchParams: { order_by: orderBy },
+ },
+ firstPageOnly: !isSemver,
+ })
+ }
+
+ static transform({ releases, isSemver, includePrereleases, displayName }) {
+ if (releases.length === 0) {
+ throw new NotFound({ prettyMessage: 'no releases found' })
+ }
+
+ const displayKey = displayName === 'tag' ? 'tag_name' : 'name'
+
+ if (!isSemver) {
+ return releases[0][displayKey]
+ }
+
+ return latest(
+ releases.map(t => t[displayKey]),
+ { pre: includePrereleases },
+ )
+ }
+
+ async handle(
+ { project },
+ {
+ gitlab_url: baseUrl = 'https://gitlab.com',
+ include_prereleases: pre,
+ sort,
+ display_name: displayName,
+ date_order_by: orderBy,
+ },
+ ) {
+ const isSemver = sort === 'semver'
+ const releases = await this.fetch({ project, baseUrl, isSemver, orderBy })
+ const version = this.constructor.transform({
+ releases,
+ isSemver,
+ includePrereleases: pre !== undefined,
+ displayName,
+ })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/gitlab/gitlab-release.spec.js b/services/gitlab/gitlab-release.spec.js
new file mode 100644
index 0000000000000..676e4ef74cd3e
--- /dev/null
+++ b/services/gitlab/gitlab-release.spec.js
@@ -0,0 +1,48 @@
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import GitLabRelease from './gitlab-release.service.js'
+
+describe('GitLabRelease', function () {
+ describe('auth', function () {
+ cleanUpNockAfterEach()
+
+ const fakeToken = 'abc123'
+ const config = {
+ public: {
+ services: {
+ gitlab: {
+ authorizedOrigins: ['https://gitlab.com'],
+ },
+ },
+ },
+ private: {
+ gitlab_token: fakeToken,
+ },
+ }
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://gitlab.com/')
+ .get('/api/v4/projects/foo%2Fbar/releases?page=1')
+ // This ensures that the expected credentials are actually being sent with the HTTP request.
+ // Without this the request wouldn't match and the test would fail.
+ .matchHeader('Authorization', `Bearer ${fakeToken}`)
+ .reply(200, [{ name: '1.9', tag_name: '1.9' }])
+
+ expect(
+ await GitLabRelease.invoke(
+ defaultContext,
+ config,
+ { project: 'foo/bar' },
+ {},
+ ),
+ ).to.deep.equal({
+ label: undefined,
+ message: 'v1.9',
+ color: 'blue',
+ })
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/gitlab/gitlab-release.tester.js b/services/gitlab/gitlab-release.tester.js
new file mode 100644
index 0000000000000..ce226926eaee0
--- /dev/null
+++ b/services/gitlab/gitlab-release.tester.js
@@ -0,0 +1,49 @@
+import { isSemver, withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+const isGitLabDisplayVersion = withRegex(/^GitLab [1-9][0-9]*.[0-9]*/)
+
+t.create('Release (latest by date)')
+ .get('/shields-ops-group/tag-test.json')
+ .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
+
+t.create('Release (nested groups latest by date)')
+ .get('/gitlab-org/frontend/eslint-plugin.json')
+ .expectBadge({ label: 'release', message: isSemver, color: 'blue' })
+
+t.create('Release (latest by date, order by created_at)')
+ .get('/shields-ops-group/tag-test.json?date_order_by=created_at')
+ .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
+
+t.create('Release (latest by date, order by released_at)')
+ .get('/shields-ops-group/tag-test.json?date_order_by=released_at')
+ .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
+
+t.create('Release (project id latest by date)')
+ .get('/29538796.json')
+ .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
+
+t.create('Release (latest by semver)')
+ .get('/shields-ops-group/tag-test.json?sort=semver')
+ .expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' })
+
+t.create('Release (latest by semver pre-release)')
+ .get('/shields-ops-group/tag-test.json?sort=semver&include_prereleases')
+ .expectBadge({ label: 'release', message: 'v5.0.0-beta.1', color: 'orange' })
+
+t.create('Release (release display name)')
+ .get('/gitlab-org/gitlab.json?display_name=release')
+ .expectBadge({ label: 'release', message: isGitLabDisplayVersion })
+
+t.create('Release (custom instance)')
+ .get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
+ .expectBadge({ label: 'release', message: isSemver, color: 'blue' })
+
+t.create('Release (project not found)')
+ .get('/fdroid/nonexistant.json')
+ .expectBadge({ label: 'release', message: 'project not found' })
+
+t.create('Release (no tags)')
+ .get('/fdroid/fdroiddata.json')
+ .expectBadge({ label: 'release', message: 'no releases found' })
diff --git a/services/gitlab/gitlab-stars.service.js b/services/gitlab/gitlab-stars.service.js
new file mode 100644
index 0000000000000..7401fb0bc228b
--- /dev/null
+++ b/services/gitlab/gitlab-stars.service.js
@@ -0,0 +1,73 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { optionalUrl, nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import GitLabBase from './gitlab-base.js'
+import { description } from './gitlab-helper.js'
+
+const schema = Joi.object({
+ star_count: nonNegativeInteger,
+}).required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabStars extends GitLabBase {
+ static category = 'social'
+
+ static route = {
+ base: 'gitlab/stars',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/stars/{project}': {
+ get: {
+ summary: 'GitLab Stars',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'stars', namedLogo: 'gitlab' }
+
+ static render({ baseUrl, project, starCount }) {
+ return {
+ message: metric(starCount),
+ style: 'social',
+ color: 'blue',
+ link: [`${baseUrl}/${project}`, `${baseUrl}/${project}/-/starrers`],
+ }
+ }
+
+ async fetch({ project, baseUrl }) {
+ // https://docs.gitlab.com/ee/api/projects.html#get-single-project
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}`,
+ httpErrors: {
+ 404: 'project not found',
+ },
+ })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ const { star_count: starCount } = await this.fetch({
+ project,
+ baseUrl,
+ })
+ return this.constructor.render({ baseUrl, project, starCount })
+ }
+}
diff --git a/services/gitlab/gitlab-stars.tester.js b/services/gitlab/gitlab-stars.tester.js
new file mode 100644
index 0000000000000..11fc18aa6f49c
--- /dev/null
+++ b/services/gitlab/gitlab-stars.tester.js
@@ -0,0 +1,35 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Stars')
+ .get('/gitlab-org/gitlab.json')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://gitlab.com/gitlab-org/gitlab',
+ 'https://gitlab.com/gitlab-org/gitlab/-/starrers',
+ ],
+ })
+
+t.create('Stars (self-managed)')
+ .get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ color: 'blue',
+ link: [
+ 'https://jihulab.com/gitlab-cn/gitlab',
+ 'https://jihulab.com/gitlab-cn/gitlab/-/starrers',
+ ],
+ })
+
+t.create('Stars (project not found)')
+ .get('/user1/gitlab-does-not-have-this-repo.json')
+ .expectBadge({
+ label: 'stars',
+ message: 'project not found',
+ })
diff --git a/services/gitlab/gitlab-tag.service.js b/services/gitlab/gitlab-tag.service.js
new file mode 100644
index 0000000000000..e013fb7763238
--- /dev/null
+++ b/services/gitlab/gitlab-tag.service.js
@@ -0,0 +1,110 @@
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { latest, renderVersionBadge } from '../version.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+import GitLabBase from './gitlab-base.js'
+
+const schema = Joi.array().items(
+ Joi.object({
+ name: Joi.string().required(),
+ }),
+)
+
+const sortEnum = ['date', 'semver']
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+ include_prereleases: Joi.equal(''),
+ sort: Joi.string()
+ .valid(...sortEnum)
+ .default('date'),
+}).required()
+
+export default class GitlabTag extends GitLabBase {
+ static category = 'version'
+
+ static route = {
+ base: 'gitlab/v/tag',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/v/tag/{project}': {
+ get: {
+ summary: 'GitLab Tag',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'shields-ops-group/tag-test',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ queryParam({
+ name: 'sort',
+ schema: { type: 'string', enum: sortEnum },
+ example: 'semver',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'tag' }
+
+ async fetch({ project, baseUrl }) {
+ // https://docs.gitlab.com/ee/api/tags.html
+ // N.B. the documentation has contradictory information about default sort order.
+ // As of 2020-10-11 the default is by date, but we add the `order_by` query param
+ // explicitly in case that changes upstream.
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
+ project,
+ )}/repository/tags`,
+ options: { searchParams: { order_by: 'updated' } },
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ static transform({ tags, sort, includePrereleases }) {
+ if (tags.length === 0) {
+ throw new NotFound({ prettyMessage: 'no tags found' })
+ }
+
+ if (sort === 'date') {
+ return tags[0].name
+ }
+
+ return latest(
+ tags.map(t => t.name),
+ { pre: includePrereleases },
+ )
+ }
+
+ async handle(
+ { project },
+ {
+ gitlab_url: baseUrl = 'https://gitlab.com',
+ include_prereleases: pre,
+ sort,
+ },
+ ) {
+ const tags = await this.fetch({ project, baseUrl })
+ const version = this.constructor.transform({
+ tags,
+ sort,
+ includePrereleases: pre !== undefined,
+ })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/gitlab/gitlab-tag.spec.js b/services/gitlab/gitlab-tag.spec.js
new file mode 100644
index 0000000000000..b0c1da012b2ba
--- /dev/null
+++ b/services/gitlab/gitlab-tag.spec.js
@@ -0,0 +1,48 @@
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import GitLabTag from './gitlab-tag.service.js'
+
+describe('GitLabTag', function () {
+ describe('auth', function () {
+ cleanUpNockAfterEach()
+
+ const fakeToken = 'abc123'
+ const config = {
+ public: {
+ services: {
+ gitlab: {
+ authorizedOrigins: ['https://gitlab.com'],
+ },
+ },
+ },
+ private: {
+ gitlab_token: fakeToken,
+ },
+ }
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://gitlab.com/')
+ .get('/api/v4/projects/foo%2Fbar/repository/tags?order_by=updated')
+ // This ensures that the expected credentials are actually being sent with the HTTP request.
+ // Without this the request wouldn't match and the test would fail.
+ .matchHeader('Authorization', `Bearer ${fakeToken}`)
+ .reply(200, [{ name: '1.9' }])
+
+ expect(
+ await GitLabTag.invoke(
+ defaultContext,
+ config,
+ { project: 'foo/bar' },
+ {},
+ ),
+ ).to.deep.equal({
+ message: 'v1.9',
+ color: 'blue',
+ label: undefined,
+ })
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/gitlab/gitlab-tag.tester.js b/services/gitlab/gitlab-tag.tester.js
new file mode 100644
index 0000000000000..f3d78bf249d20
--- /dev/null
+++ b/services/gitlab/gitlab-tag.tester.js
@@ -0,0 +1,35 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Tag (latest by date)')
+ .get('/shields-ops-group/tag-test.json')
+ .expectBadge({ label: 'tag', message: 'v2.0.0', color: 'blue' })
+
+t.create('Tag (nested groups)')
+ .get('/megabyte-labs/docker/ci-pipeline/ansible.json')
+ .expectBadge({ label: 'tag', message: isSemver, color: 'blue' })
+
+t.create('Tag (project id latest by date)')
+ .get('/29538796.json')
+ .expectBadge({ label: 'tag', message: 'v2.0.0', color: 'blue' })
+
+t.create('Tag (latest by SemVer)')
+ .get('/shields-ops-group/tag-test.json?sort=semver')
+ .expectBadge({ label: 'tag', message: 'v4.0.0', color: 'blue' })
+
+t.create('Tag (latest by SemVer pre-release)')
+ .get('/shields-ops-group/tag-test.json?sort=semver&include_prereleases')
+ .expectBadge({ label: 'tag', message: 'v5.0.0-beta.1', color: 'orange' })
+
+t.create('Tag (custom instance)')
+ .get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
+ .expectBadge({ label: 'tag', message: isSemver, color: 'blue' })
+
+t.create('Tag (repo not found)')
+ .get('/fdroid/nonexistant.json')
+ .expectBadge({ label: 'tag', message: 'project not found' })
+
+t.create('Tag (no tags)')
+ .get('/fdroid/fdroiddata.json')
+ .expectBadge({ label: 'tag', message: 'no tags found' })
diff --git a/services/gitlab/gitlab-top-language.service.js b/services/gitlab/gitlab-top-language.service.js
new file mode 100644
index 0000000000000..e68d5690a791c
--- /dev/null
+++ b/services/gitlab/gitlab-top-language.service.js
@@ -0,0 +1,79 @@
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import GitLabBase from './gitlab-base.js'
+import { description, httpErrorsFor } from './gitlab-helper.js'
+
+const schema = Joi.object()
+ .pattern(
+ Joi.string().required(),
+ Joi.number().min(0).max(100).precision(2).required(),
+ )
+ .required()
+
+const queryParamSchema = Joi.object({
+ gitlab_url: optionalUrl,
+}).required()
+
+export default class GitlabTopLanguage extends GitLabBase {
+ static category = 'analysis'
+
+ static route = {
+ base: 'gitlab/languages',
+ pattern: ':project+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/gitlab/languages/{project}': {
+ get: {
+ summary: 'GitLab Top Language',
+ description,
+ parameters: [
+ pathParam({
+ name: 'project',
+ example: 'gitlab-org/gitlab',
+ }),
+ queryParam({
+ name: 'gitlab_url',
+ example: 'https://gitlab.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'language' }
+
+ static render({ languageData }) {
+ const topLanguage = Object.keys(languageData).reduce((a, b) =>
+ languageData[a] > languageData[b] ? a : b,
+ )
+ return {
+ label: topLanguage.toLowerCase(),
+ message: `${languageData[topLanguage].toFixed(1)}%`,
+ color: 'blue',
+ }
+ }
+
+ async fetch({ project, baseUrl }) {
+ return super.fetch({
+ schema,
+ url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}/languages`,
+ httpErrors: httpErrorsFor('project not found'),
+ })
+ }
+
+ async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
+ const languageData = await this.fetch({
+ project,
+ baseUrl,
+ })
+
+ if (Object.keys(languageData).length > 0) {
+ return this.constructor.render({ languageData })
+ } else {
+ throw new InvalidResponse({ prettyMessage: 'no languages found' })
+ }
+ }
+}
diff --git a/services/gitlab/gitlab-top-language.tester.js b/services/gitlab/gitlab-top-language.tester.js
new file mode 100644
index 0000000000000..43951fd615bbc
--- /dev/null
+++ b/services/gitlab/gitlab-top-language.tester.js
@@ -0,0 +1,23 @@
+import { createServiceTester } from '../tester.js'
+import { isDecimalPercentage } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Valid Repository').get('/wireshark/wireshark.json').expectBadge({
+ label: 'c',
+ message: isDecimalPercentage,
+})
+
+t.create('Valid Blank Repo')
+ .get('/KoruptTinker/gitlab-blank-repo.json')
+ .expectBadge({
+ label: 'language',
+ message: 'no languages found',
+ })
+
+t.create('Invalid Repository')
+ .get('/wireshark/invalidexample.json')
+ .expectBadge({
+ label: 'language',
+ message: 'project not found',
+ })
diff --git a/services/gitter/gitter.service.js b/services/gitter/gitter.service.js
index 9a54a2a4d9989..70abfd96ea500 100644
--- a/services/gitter/gitter.service.js
+++ b/services/gitter/gitter.service.js
@@ -1,35 +1,32 @@
-'use strict'
+import { BaseStaticService, pathParams } from '../index.js'
-const { BaseStaticService } = require('..')
+export default class Gitter extends BaseStaticService {
+ static category = 'chat'
-module.exports = class Gitter extends BaseStaticService {
- static get category() {
- return 'chat'
+ static route = {
+ base: 'gitter/room',
+ pattern: ':user/:repo',
}
- static get route() {
- return {
- base: 'gitter/room',
- pattern: ':user/:repo',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Gitter',
- namedParams: {
- user: 'nwjs',
- repo: 'nw.js',
- },
- staticPreview: this.render(),
+ static openApi = {
+ '/gitter/room/{user}/{repo}': {
+ get: {
+ summary: 'Gitter',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'nwjs',
+ },
+ {
+ name: 'repo',
+ example: 'nw.js',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'chat' }
- }
+ static defaultBadgeData = { label: 'chat' }
static render() {
return { message: 'on gitter', color: 'brightgreen' }
diff --git a/services/gitter/gitter.tester.js b/services/gitter/gitter.tester.js
index 84351df21234f..53c8a9cdd9138 100644
--- a/services/gitter/gitter.tester.js
+++ b/services/gitter/gitter.tester.js
@@ -1,10 +1,7 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('on gitter')
- .get('/nwjs/nw.js.json')
- .expectBadge({
- label: 'chat',
- message: 'on gitter',
- })
+t.create('on gitter').get('/nwjs/nw.js.json').expectBadge({
+ label: 'chat',
+ message: 'on gitter',
+})
diff --git a/services/gnome-extensions/gnome-extensions-downloads.service.js b/services/gnome-extensions/gnome-extensions-downloads.service.js
new file mode 100644
index 0000000000000..c7551da501149
--- /dev/null
+++ b/services/gnome-extensions/gnome-extensions-downloads.service.js
@@ -0,0 +1,50 @@
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const extensionSchema = Joi.object({
+ downloads: Joi.number().required(),
+})
+
+export default class GnomeExtensionsDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'gnome-extensions/dt',
+ pattern: ':extensionId',
+ }
+
+ static openApi = {
+ '/gnome-extensions/dt/{extensionId}': {
+ get: {
+ summary: 'Gnome Extensions Downloads',
+ parameters: pathParams({
+ name: 'extensionId',
+ description: 'Id of the Gnome Extension',
+ example: 'just-perfection-desktop@just-perfection',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ static render({ downloads }) {
+ return renderDownloadsBadge({ downloads })
+ }
+
+ async getExtension({ extensionId }) {
+ return await this._requestJson({
+ schema: extensionSchema,
+ url: `https://extensions.gnome.org/api/v1/extensions/${extensionId}/`,
+ httpErrors: {
+ 404: 'extension not found',
+ },
+ })
+ }
+
+ async handle({ extensionId }) {
+ const { downloads } = await this.getExtension({ extensionId })
+ return this.constructor.render({ downloads })
+ }
+}
diff --git a/services/gnome-extensions/gnome-extensions-downloads.tester.js b/services/gnome-extensions/gnome-extensions-downloads.tester.js
new file mode 100644
index 0000000000000..856e915081e68
--- /dev/null
+++ b/services/gnome-extensions/gnome-extensions-downloads.tester.js
@@ -0,0 +1,13 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Downloads')
+ .get('/just-perfection-desktop@just-perfection.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('Downloads (not found)').get('/non-existent.json').expectBadge({
+ label: 'downloads',
+ message: 'extension not found',
+})
diff --git a/services/gnome-extensions/gnome-extensions-version.service.js b/services/gnome-extensions/gnome-extensions-version.service.js
new file mode 100644
index 0000000000000..14aa28416cd0a
--- /dev/null
+++ b/services/gnome-extensions/gnome-extensions-version.service.js
@@ -0,0 +1,68 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
+
+const versionSchema = Joi.object({
+ results: Joi.array()
+ .items(
+ Joi.object({
+ version: Joi.number().required(),
+ version_name: Joi.string().allow(null).required(),
+ status: Joi.number().required(),
+ }).unknown(true),
+ )
+ .required(),
+}).unknown(true)
+
+const ACTIVE_STATUS = 3
+
+export default class GnomeExtensionsVersion extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'gnome-extensions/v',
+ pattern: ':extensionId',
+ }
+
+ static openApi = {
+ '/gnome-extensions/v/{extensionId}': {
+ get: {
+ summary: 'Gnome Extensions Version',
+ parameters: pathParams({
+ name: 'extensionId',
+ description: 'Id of the Gnome Extension',
+ example: 'just-perfection-desktop@just-perfection',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'version' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async fetch({ extensionId }) {
+ return await this._requestJson({
+ schema: versionSchema,
+ url: `https://extensions.gnome.org/api/v1/extensions/${extensionId}/versions/?page_size=100`,
+ httpErrors: {
+ 404: 'extension not found',
+ },
+ })
+ }
+
+ async handle({ extensionId }) {
+ const { results } = await this.fetch({ extensionId })
+ const activeVersions = results.filter(r => r.status === ACTIVE_STATUS)
+ if (activeVersions.length === 0) {
+ throw new NotFound({ prettyMessage: 'no active version found' })
+ }
+ const latest = activeVersions.reduce((a, b) =>
+ a.version > b.version ? a : b,
+ )
+ const version = latest.version_name ?? String(latest.version)
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/gnome-extensions/gnome-extensions-version.tester.js b/services/gnome-extensions/gnome-extensions-version.tester.js
new file mode 100644
index 0000000000000..6a29f76695375
--- /dev/null
+++ b/services/gnome-extensions/gnome-extensions-version.tester.js
@@ -0,0 +1,26 @@
+import { createServiceTester } from '../tester.js'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Version')
+ .get('/just-perfection-desktop@just-perfection.json')
+ .expectBadge({ label: 'version', message: isVPlusDottedVersionAtLeastOne })
+
+t.create('Version (not found)').get('/non-existent.json').expectBadge({
+ label: 'version',
+ message: 'no active version found',
+})
+
+t.create('No active versions')
+ .get('/no-active.json')
+ .intercept(nock =>
+ nock('https://extensions.gnome.org')
+ .get('/api/v1/extensions/no-active/versions/')
+ .query({ page_size: '100' })
+ .reply(200, { results: [] }),
+ )
+ .expectBadge({
+ label: 'version',
+ message: 'no active version found',
+ })
diff --git a/services/gradle-plugin-portal/gradle-plugin-portal.service.js b/services/gradle-plugin-portal/gradle-plugin-portal.service.js
new file mode 100644
index 0000000000000..02b51161317da
--- /dev/null
+++ b/services/gradle-plugin-portal/gradle-plugin-portal.service.js
@@ -0,0 +1,34 @@
+import { redirector, pathParam } from '../index.js'
+import { commonParams } from '../maven-metadata/maven-metadata.js'
+
+export default redirector({
+ category: 'version',
+ isRetired: false,
+ route: {
+ base: 'gradle-plugin-portal/v',
+ pattern: ':pluginId',
+ },
+ openApi: {
+ '/gradle-plugin-portal/v/{pluginId}': {
+ get: {
+ summary: 'Gradle Plugin Portal Version',
+ parameters: [
+ pathParam({ name: 'pluginId', example: 'com.gradle.plugin-publish' }),
+ ...commonParams,
+ ],
+ },
+ },
+ },
+ transformPath: () => '/maven-metadata/v',
+ transformQueryParams: ({ pluginId }) => {
+ const groupPath = pluginId.replace(/\./g, '/')
+ const artifactId = `${pluginId}.gradle.plugin`
+ const metadataUrl = `https://plugins.gradle.org/m2/${groupPath}/${artifactId}/maven-metadata.xml`
+ return {
+ metadataUrl,
+ label: 'plugin portal',
+ }
+ },
+ overrideTransformedQueryParams: true,
+ dateAdded: new Date('2021-05-30'),
+})
diff --git a/services/gradle-plugin-portal/gradle-plugin-portal.tester.js b/services/gradle-plugin-portal/gradle-plugin-portal.tester.js
new file mode 100644
index 0000000000000..316359c1351ff
--- /dev/null
+++ b/services/gradle-plugin-portal/gradle-plugin-portal.tester.js
@@ -0,0 +1,26 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('gradle plugin portal')
+ .get('/com.gradle.plugin-publish')
+ .expectRedirect(
+ `/maven-metadata/v.svg?metadataUrl=${encodeURIComponent(
+ 'https://plugins.gradle.org/m2/com/gradle/plugin-publish/com.gradle.plugin-publish.gradle.plugin/maven-metadata.xml',
+ )}&label=plugin%20portal`,
+ )
+
+t.create('gradle plugin portal with custom labels')
+ .get('/com.gradle.plugin-publish?label=custom%20label')
+ .expectRedirect(
+ `/maven-metadata/v.svg?metadataUrl=${encodeURIComponent(
+ 'https://plugins.gradle.org/m2/com/gradle/plugin-publish/com.gradle.plugin-publish.gradle.plugin/maven-metadata.xml',
+ )}&label=custom%20label`,
+ )
+
+t.create('gradle plugin portal with custom color')
+ .get('/com.gradle.plugin-publish?color=gray')
+ .expectRedirect(
+ `/maven-metadata/v.svg?metadataUrl=${encodeURIComponent(
+ 'https://plugins.gradle.org/m2/com/gradle/plugin-publish/com.gradle.plugin-publish.gradle.plugin/maven-metadata.xml',
+ )}&label=plugin%20portal&color=gray`,
+ )
diff --git a/services/gratipay/gratipay.service.js b/services/gratipay/gratipay.service.js
deleted file mode 100644
index 3ad1964d72d0a..0000000000000
--- a/services/gratipay/gratipay.service.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-const commonAttrs = {
- category: 'funding',
- label: 'gratipay',
- dateAdded: new Date('2017-12-29'),
-}
-
-module.exports = [
- deprecatedService({
- route: {
- base: 'gittip',
- pattern: ':various*',
- },
- ...commonAttrs,
- }),
- deprecatedService({
- route: {
- base: 'gratipay',
- pattern: ':various*',
- },
- ...commonAttrs,
- }),
-]
diff --git a/services/gratipay/gratipay.tester.js b/services/gratipay/gratipay.tester.js
deleted file mode 100644
index 522ef69011de6..0000000000000
--- a/services/gratipay/gratipay.tester.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'gratipay',
- title: 'Gratipay',
-}))
-
-t.create('Receiving')
- .get('/Gratipay.json')
- .expectBadge({
- label: 'gratipay',
- message: 'no longer available',
- })
diff --git a/services/greasyfork/greasyfork-base.js b/services/greasyfork/greasyfork-base.js
new file mode 100644
index 0000000000000..ff1ae77394836
--- /dev/null
+++ b/services/greasyfork/greasyfork-base.js
@@ -0,0 +1,32 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound } from '../index.js'
+
+const schema = Joi.object({
+ daily_installs: nonNegativeInteger,
+ total_installs: nonNegativeInteger,
+ good_ratings: nonNegativeInteger,
+ ok_ratings: nonNegativeInteger,
+ bad_ratings: nonNegativeInteger,
+ version: Joi.string().required(),
+ license: Joi.string().allow(null).required(),
+}).required()
+
+export default class BaseGreasyForkService extends BaseJsonService {
+ static defaultBadgeData = { label: 'greasy fork' }
+
+ async fetch({ scriptId }) {
+ try {
+ return await this._requestJson({
+ schema,
+ url: `https://greasyfork.org/scripts/${scriptId}.json`,
+ })
+ } catch (e) {
+ if (!(e instanceof NotFound)) throw e
+ return this._requestJson({
+ schema,
+ url: `https://sleazyfork.org/scripts/${scriptId}.json`,
+ })
+ }
+ }
+}
diff --git a/services/greasyfork/greasyfork-downloads.service.js b/services/greasyfork/greasyfork-downloads.service.js
new file mode 100644
index 0000000000000..c49c056252007
--- /dev/null
+++ b/services/greasyfork/greasyfork-downloads.service.js
@@ -0,0 +1,41 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import BaseGreasyForkService from './greasyfork-base.js'
+
+export default class GreasyForkInstalls extends BaseGreasyForkService {
+ static category = 'downloads'
+ static route = { base: 'greasyfork', pattern: ':variant(dt|dd)/:scriptId' }
+
+ static openApi = {
+ '/greasyfork/{variant}/{scriptId}': {
+ get: {
+ summary: 'Greasy Fork Downloads',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'dt',
+ description: 'total downloads or daily downloads',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'scriptId',
+ example: '406540',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'installs' }
+
+ async handle({ variant, scriptId }) {
+ const data = await this.fetch({ scriptId })
+ if (variant === 'dd') {
+ const downloads = data.daily_installs
+ const interval = 'day'
+ return renderDownloadsBadge({ downloads, interval })
+ }
+ const downloads = data.total_installs
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/greasyfork/greasyfork-downloads.tester.js b/services/greasyfork/greasyfork-downloads.tester.js
new file mode 100644
index 0000000000000..da6c49f4259d0
--- /dev/null
+++ b/services/greasyfork/greasyfork-downloads.tester.js
@@ -0,0 +1,23 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Daily Installs')
+ .get('/dd/406540.json')
+ .expectBadge({ label: 'installs', message: isMetricOverTimePeriod })
+
+t.create('Daily Installs (not found)')
+ .get('/dd/000000.json')
+ .expectBadge({ label: 'installs', message: 'not found' })
+
+t.create('Total Installs')
+ .get('/dt/406540.json')
+ .expectBadge({ label: 'installs', message: isMetric })
+
+t.create('Total Installs (not found)')
+ .get('/dt/000000.json')
+ .expectBadge({ label: 'installs', message: 'not found' })
+
+t.create('Total Installs (sleazyfork)')
+ .get('/dt/374903.json')
+ .expectBadge({ label: 'installs', message: isMetric })
diff --git a/services/greasyfork/greasyfork-license.service.js b/services/greasyfork/greasyfork-license.service.js
new file mode 100644
index 0000000000000..c9906b4929f97
--- /dev/null
+++ b/services/greasyfork/greasyfork-license.service.js
@@ -0,0 +1,38 @@
+import { renderLicenseBadge } from '../licenses.js'
+import { InvalidResponse, pathParams } from '../index.js'
+import BaseGreasyForkService from './greasyfork-base.js'
+
+export default class GreasyForkLicense extends BaseGreasyForkService {
+ static category = 'license'
+ static route = { base: 'greasyfork', pattern: 'l/:scriptId' }
+
+ static openApi = {
+ '/greasyfork/l/{scriptId}': {
+ get: {
+ summary: 'Greasy Fork License',
+ parameters: pathParams({
+ name: 'scriptId',
+ example: '406540',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'license' }
+
+ transform({ data }) {
+ if (data.license === null) {
+ throw new InvalidResponse({
+ prettyMessage: 'license not found',
+ })
+ }
+ // remove suffix " License" from data.license
+ return { license: data.license.replace(/ License$/, '') }
+ }
+
+ async handle({ scriptId }) {
+ const data = await this.fetch({ scriptId })
+ const { license } = this.transform({ data })
+ return renderLicenseBadge({ licenses: [license] })
+ }
+}
diff --git a/services/greasyfork/greasyfork-license.tester.js b/services/greasyfork/greasyfork-license.tester.js
new file mode 100644
index 0000000000000..ee764424041d2
--- /dev/null
+++ b/services/greasyfork/greasyfork-license.tester.js
@@ -0,0 +1,11 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('License (valid)').get('/l/406540.json').expectBadge({
+ label: 'license',
+ message: 'MIT',
+})
+
+t.create('License (not found)')
+ .get('/l/000000.json')
+ .expectBadge({ label: 'license', message: 'not found' })
diff --git a/services/greasyfork/greasyfork-rating.service.js b/services/greasyfork/greasyfork-rating.service.js
new file mode 100644
index 0000000000000..f56e79441438a
--- /dev/null
+++ b/services/greasyfork/greasyfork-rating.service.js
@@ -0,0 +1,45 @@
+import { pathParams } from '../index.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { metric } from '../text-formatters.js'
+import BaseGreasyForkService from './greasyfork-base.js'
+
+export default class GreasyForkRatingCount extends BaseGreasyForkService {
+ static category = 'rating'
+ static route = { base: 'greasyfork', pattern: 'rating-count/:scriptId' }
+
+ static openApi = {
+ '/greasyfork/rating-count/{scriptId}': {
+ get: {
+ summary: 'Greasy Fork Rating',
+ parameters: pathParams({
+ name: 'scriptId',
+ example: '406540',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'rating' }
+
+ static render({ good, ok, bad }) {
+ let color = 'lightgrey'
+ const total = good + bad + ok
+ if (total > 0) {
+ const score = (good * 3 + ok * 2 + bad * 1) / total - 1
+ color = floorCountColor(score, 1, 1.5, 2)
+ }
+ return {
+ message: `${metric(good)} good, ${metric(ok)} ok, ${metric(bad)} bad`,
+ color,
+ }
+ }
+
+ async handle({ scriptId }) {
+ const data = await this.fetch({ scriptId })
+ return this.constructor.render({
+ good: data.good_ratings,
+ ok: data.ok_ratings,
+ bad: data.bad_ratings,
+ })
+ }
+}
diff --git a/services/greasyfork/greasyfork-rating.spec.js b/services/greasyfork/greasyfork-rating.spec.js
new file mode 100644
index 0000000000000..459cb72982ee0
--- /dev/null
+++ b/services/greasyfork/greasyfork-rating.spec.js
@@ -0,0 +1,31 @@
+import { test, given } from 'sazerac'
+import GreasyForkRatingCount from './greasyfork-rating.service.js'
+
+describe('GreasyForkRatingCount', function () {
+ test(GreasyForkRatingCount.render, () => {
+ given({ good: 0, ok: 0, bad: 30 }).expect({
+ message: '0 good, 0 ok, 30 bad',
+ color: 'red',
+ })
+ given({ good: 10, ok: 20, bad: 30 }).expect({
+ message: '10 good, 20 ok, 30 bad',
+ color: 'yellow',
+ })
+ given({ good: 10, ok: 20, bad: 10 }).expect({
+ message: '10 good, 20 ok, 10 bad',
+ color: 'yellowgreen',
+ })
+ given({ good: 20, ok: 10, bad: 0 }).expect({
+ message: '20 good, 10 ok, 0 bad',
+ color: 'green',
+ })
+ given({ good: 30, ok: 0, bad: 0 }).expect({
+ message: '30 good, 0 ok, 0 bad',
+ color: 'brightgreen',
+ })
+ given({ good: 0, ok: 0, bad: 0 }).expect({
+ message: '0 good, 0 ok, 0 bad',
+ color: 'lightgrey',
+ })
+ })
+})
diff --git a/services/greasyfork/greasyfork-rating.tester.js b/services/greasyfork/greasyfork-rating.tester.js
new file mode 100644
index 0000000000000..41dee4c6e545e
--- /dev/null
+++ b/services/greasyfork/greasyfork-rating.tester.js
@@ -0,0 +1,14 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Rating Count')
+ .get('/rating-count/406540.json')
+ .expectBadge({
+ label: 'rating',
+ message: Joi.string().regex(/^\d+ good, \d+ ok, \d+ bad$/),
+ })
+
+t.create('Rating Count (not found)')
+ .get('/rating-count/000000.json')
+ .expectBadge({ label: 'rating', message: 'not found' })
diff --git a/services/greasyfork/greasyfork-version.service.js b/services/greasyfork/greasyfork-version.service.js
new file mode 100644
index 0000000000000..9831badd4efbc
--- /dev/null
+++ b/services/greasyfork/greasyfork-version.service.js
@@ -0,0 +1,25 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import BaseGreasyForkService from './greasyfork-base.js'
+
+export default class GreasyForkVersion extends BaseGreasyForkService {
+ static category = 'version'
+ static route = { base: 'greasyfork', pattern: 'v/:scriptId' }
+
+ static openApi = {
+ '/greasyfork/v/{scriptId}': {
+ get: {
+ summary: 'Greasy Fork Version',
+ parameters: pathParams({
+ name: 'scriptId',
+ example: '406540',
+ }),
+ },
+ },
+ }
+
+ async handle({ scriptId }) {
+ const data = await this.fetch({ scriptId })
+ return renderVersionBadge({ version: data.version })
+ }
+}
diff --git a/services/greasyfork/greasyfork-version.tester.js b/services/greasyfork/greasyfork-version.tester.js
new file mode 100644
index 0000000000000..5ea8330d56898
--- /dev/null
+++ b/services/greasyfork/greasyfork-version.tester.js
@@ -0,0 +1,12 @@
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Version').get('/v/406540.json').expectBadge({
+ label: 'greasy fork',
+ message: isVPlusDottedVersionAtLeastOne,
+})
+
+t.create('Version (not found)')
+ .get('/v/000000.json')
+ .expectBadge({ label: 'greasy fork', message: 'not found' })
diff --git a/services/hackage/hackage-deps.service.js b/services/hackage/hackage-deps.service.js
deleted file mode 100644
index fd03264c5a3b6..0000000000000
--- a/services/hackage/hackage-deps.service.js
+++ /dev/null
@@ -1,54 +0,0 @@
-'use strict'
-
-const { BaseService } = require('..')
-
-module.exports = class HackageDeps extends BaseService {
- static get category() {
- return 'dependencies'
- }
-
- static get route() {
- return {
- base: 'hackage-deps/v',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Hackage-Deps',
- namedParams: { packageName: 'lens' },
- staticPreview: this.render({ isOutdated: false }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'dependencies' }
- }
-
- static render({ isOutdated }) {
- if (isOutdated) {
- return { message: 'outdated', color: 'orange' }
- } else {
- return { message: 'up to date', color: 'brightgreen' }
- }
- }
-
- async handle({ packageName }) {
- const reverseUrl = `http://packdeps.haskellers.com/licenses/${packageName}`
- const feedUrl = `http://packdeps.haskellers.com/feed/${packageName}`
-
- // first call /reverse to check if the package exists
- // this will throw a 404 if it doesn't
- await this._request({ url: reverseUrl })
-
- // if the package exists, then query /feed to check the dependencies
- const { buffer } = await this._request({ url: feedUrl })
-
- const outdatedStr = `Outdated dependencies for ${packageName} `
- const isOutdated = buffer.includes(outdatedStr)
- return this.constructor.render({ isOutdated })
- }
-}
diff --git a/services/hackage/hackage-deps.tester.js b/services/hackage/hackage-deps.tester.js
deleted file mode 100644
index 3b60d7c6eac9f..0000000000000
--- a/services/hackage/hackage-deps.tester.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('hackage deps (valid)')
- .get('/lens.json')
- .expectBadge({
- label: 'dependencies',
- message: Joi.string().regex(/^(up to date|outdated)$/),
- })
-
-t.create('hackage deps (not found)')
- .get('/not-a-package.json')
- .expectBadge({ label: 'dependencies', message: 'not found' })
diff --git a/services/hackage/hackage-version.service.js b/services/hackage/hackage-version.service.js
index 46321ffb6c83c..9a586d9e77e72 100644
--- a/services/hackage/hackage-version.service.js
+++ b/services/hackage/hackage-version.service.js
@@ -1,33 +1,27 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { BaseService, InvalidResponse, pathParams } from '../index.js'
-const { renderVersionBadge } = require('../version')
-const { BaseService, InvalidResponse } = require('..')
+export default class HackageVersion extends BaseService {
+ static category = 'version'
-module.exports = class HackageVersion extends BaseService {
- static get category() {
- return 'version'
+ static route = {
+ base: 'hackage/v',
+ pattern: ':packageName',
}
- static get route() {
- return {
- base: 'hackage/v',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Hackage',
- namedParams: { packageName: 'lens' },
- staticPreview: renderVersionBadge({ version: '4.1.7' }),
+ static openApi = {
+ '/hackage/v/{packageName}': {
+ get: {
+ summary: 'Hackage Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'lens',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'hackage' }
- }
+ static defaultBadgeData = { label: 'hackage' }
async fetch({ packageName }) {
return this._request({
diff --git a/services/hackage/hackage-version.tester.js b/services/hackage/hackage-version.tester.js
index b1092ef402f20..5f66211a3774e 100644
--- a/services/hackage/hackage-version.tester.js
+++ b/services/hackage/hackage-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('hackage version (valid)')
- .get('/lens.json')
- .expectBadge({
- label: 'hackage',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('hackage version (valid)').get('/lens.json').expectBadge({
+ label: 'hackage',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('hackage version (not found)')
.get('/not-a-package.json')
@@ -19,6 +16,6 @@ t.create('hackage version (unexpected response)')
.intercept(nock =>
nock('https://hackage.haskell.org')
.get('/package/lens/lens.cabal')
- .reply(200, '')
+ .reply(200, ''),
)
.expectBadge({ label: 'hackage', message: 'invalid response data' })
diff --git a/services/hackernews/hackernews-user-karma.service.js b/services/hackernews/hackernews-user-karma.service.js
new file mode 100644
index 0000000000000..7444662a4ffef
--- /dev/null
+++ b/services/hackernews/hackernews-user-karma.service.js
@@ -0,0 +1,68 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
+import { anyInteger } from '../validators.js'
+
+const schema = Joi.object({
+ karma: anyInteger,
+})
+ .allow(null)
+ .required()
+
+export default class HackerNewsUserKarma extends BaseJsonService {
+ static category = 'social'
+
+ static route = {
+ base: 'hackernews/user-karma',
+ pattern: ':id',
+ }
+
+ static openApi = {
+ '/hackernews/user-karma/{id}': {
+ get: {
+ summary: 'HackerNews User Karma',
+ parameters: pathParams({
+ name: 'id',
+ example: 'pg',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'Karma',
+ namedLogo: 'ycombinator',
+ }
+
+ static render({ karma, id }) {
+ const color = karma > 0 ? 'brightgreen' : karma === 0 ? 'orange' : 'red'
+ return {
+ label: `U/${id} karma`,
+ message: metric(karma),
+ color,
+ style: 'social',
+ }
+ }
+
+ async fetch({ id }) {
+ return this._requestJson({
+ schema,
+ url: `https://hacker-news.firebaseio.com/v0/user/${id}.json`,
+ httpErrors: {
+ 404: 'user not found',
+ },
+ })
+ }
+
+ async handle({ id }) {
+ const json = await this.fetch({ id })
+ if (json == null) {
+ throw new NotFound({ prettyMessage: 'user not found' })
+ }
+ const { karma } = json
+ return this.constructor.render({
+ karma,
+ id,
+ })
+ }
+}
diff --git a/services/hackernews/hackernews-user-karma.tester.js b/services/hackernews/hackernews-user-karma.tester.js
new file mode 100644
index 0000000000000..d4eb227bfaa90
--- /dev/null
+++ b/services/hackernews/hackernews-user-karma.tester.js
@@ -0,0 +1,26 @@
+import { createServiceTester } from '../tester.js'
+import { isMetricAllowNegative } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('valid repo').get('/pg.json').expectBadge({
+ label: 'U/pg karma',
+ message: isMetricAllowNegative,
+})
+
+t.create('valid repo -- negative karma')
+ .get('/negative.json')
+ .intercept(nock =>
+ nock('https://hacker-news.firebaseio.com/v0/user')
+ .get('/negative.json')
+ .reply(200, { karma: -1234 }),
+ )
+ .expectBadge({
+ label: 'U/negative karma',
+ message: isMetricAllowNegative,
+ })
+
+t.create('invalid user').get('/hopefullythisdoesnotexist.json').expectBadge({
+ label: 'Karma',
+ message: 'user not found',
+})
diff --git a/services/hangar/hangar-base.js b/services/hangar/hangar-base.js
new file mode 100644
index 0000000000000..54f3b11b63c01
--- /dev/null
+++ b/services/hangar/hangar-base.js
@@ -0,0 +1,37 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+
+const description = `
+Hangar is a plugin repository for the Paper, Waterfall and Folia platforms.
`
+
+const resourceSchema = Joi.object({
+ stats: Joi.object({
+ views: Joi.number().required(),
+ downloads: Joi.number().required(),
+ recentViews: Joi.number().required(),
+ recentDownloads: Joi.number().required(),
+ stars: Joi.number().required(),
+ watchers: Joi.number().required(),
+ }).required(),
+}).required()
+
+class BaseHangarService extends BaseJsonService {
+ static _cacheLength = 3600
+
+ async fetch({
+ slug,
+ schema = resourceSchema,
+ url = `https://hangar.papermc.io/api/v1/projects/${slug}`,
+ }) {
+ return this._requestJson({
+ schema,
+ url,
+ httpErrors: {
+ 401: 'Api session missing, invalid or expired',
+ 403: 'Not enough permission to use this endpoint',
+ },
+ })
+ }
+}
+
+export { description, BaseHangarService }
diff --git a/services/hangar/hangar-downloads.service.js b/services/hangar/hangar-downloads.service.js
new file mode 100644
index 0000000000000..b5089b8bfe435
--- /dev/null
+++ b/services/hangar/hangar-downloads.service.js
@@ -0,0 +1,34 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseHangarService, description } from './hangar-base.js'
+
+export default class HangarDownloads extends BaseHangarService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'hangar/dt',
+ pattern: ':slug',
+ }
+
+ static openApi = {
+ '/hangar/dt/{slug}': {
+ get: {
+ summary: 'Hangar Downloads',
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: 'Essentials',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ slug }) {
+ const {
+ stats: { downloads },
+ } = await this.fetch({ slug })
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/hangar/hangar-downloads.tester.js b/services/hangar/hangar-downloads.tester.js
new file mode 100644
index 0000000000000..ee8c8b6aa2e3f
--- /dev/null
+++ b/services/hangar/hangar-downloads.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Essentials').get('/Essentials.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('Invalid Resource').get('/does-not-exist.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
diff --git a/services/hangar/hangar-stars.service.js b/services/hangar/hangar-stars.service.js
new file mode 100644
index 0000000000000..715caa63f0ca3
--- /dev/null
+++ b/services/hangar/hangar-stars.service.js
@@ -0,0 +1,43 @@
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseHangarService, description } from './hangar-base.js'
+
+export default class HangarStars extends BaseHangarService {
+ static category = 'social'
+
+ static route = {
+ base: 'hangar/stars',
+ pattern: ':slug',
+ }
+
+ static openApi = {
+ '/hangar/stars/{slug}': {
+ get: {
+ summary: 'Hangar Stars',
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: 'Essentials',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'stars',
+ color: 'blue',
+ }
+
+ static render({ stars }) {
+ return {
+ message: metric(stars),
+ }
+ }
+
+ async handle({ slug }) {
+ const {
+ stats: { stars },
+ } = await this.fetch({ slug })
+ return this.constructor.render({ stars })
+ }
+}
diff --git a/services/hangar/hangar-stars.tester.js b/services/hangar/hangar-stars.tester.js
new file mode 100644
index 0000000000000..d8bc3b496eaea
--- /dev/null
+++ b/services/hangar/hangar-stars.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Essentials').get('/Essentials.json').expectBadge({
+ label: 'stars',
+ message: isMetric,
+})
+
+t.create('Invalid Resource').get('/does-not-exist.json').expectBadge({
+ label: 'stars',
+ message: 'not found',
+})
diff --git a/services/hangar/hangar-views.service.js b/services/hangar/hangar-views.service.js
new file mode 100644
index 0000000000000..3794d7a7786e4
--- /dev/null
+++ b/services/hangar/hangar-views.service.js
@@ -0,0 +1,43 @@
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseHangarService, description } from './hangar-base.js'
+
+export default class HangarViews extends BaseHangarService {
+ static category = 'other'
+
+ static route = {
+ base: 'hangar/views',
+ pattern: ':slug',
+ }
+
+ static openApi = {
+ '/hangar/views/{slug}': {
+ get: {
+ summary: 'Hangar Views',
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: 'Essentials',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'views',
+ color: 'blue',
+ }
+
+ static render({ views }) {
+ return {
+ message: metric(views),
+ }
+ }
+
+ async handle({ slug }) {
+ const {
+ stats: { views },
+ } = await this.fetch({ slug })
+ return this.constructor.render({ views })
+ }
+}
diff --git a/services/hangar/hangar-views.tester.js b/services/hangar/hangar-views.tester.js
new file mode 100644
index 0000000000000..af899f7c50b22
--- /dev/null
+++ b/services/hangar/hangar-views.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Essentials').get('/Essentials.json').expectBadge({
+ label: 'views',
+ message: isMetric,
+})
+
+t.create('Invalid Resource').get('/does-not-exist.json').expectBadge({
+ label: 'views',
+ message: 'not found',
+})
diff --git a/services/hangar/hangar-watchers.service.js b/services/hangar/hangar-watchers.service.js
new file mode 100644
index 0000000000000..38939e659b2a1
--- /dev/null
+++ b/services/hangar/hangar-watchers.service.js
@@ -0,0 +1,43 @@
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseHangarService, description } from './hangar-base.js'
+
+export default class HangarWatchers extends BaseHangarService {
+ static category = 'social'
+
+ static route = {
+ base: 'hangar/watchers',
+ pattern: ':slug',
+ }
+
+ static openApi = {
+ '/hangar/watchers/{slug}': {
+ get: {
+ summary: 'Hangar Watchers',
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: 'Essentials',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'watchers',
+ color: 'blue',
+ }
+
+ static render({ watchers }) {
+ return {
+ message: metric(watchers),
+ }
+ }
+
+ async handle({ slug }) {
+ const {
+ stats: { watchers },
+ } = await this.fetch({ slug })
+ return this.constructor.render({ watchers })
+ }
+}
diff --git a/services/hangar/hangar-watchers.tester.js b/services/hangar/hangar-watchers.tester.js
new file mode 100644
index 0000000000000..fdc6c23c4fef7
--- /dev/null
+++ b/services/hangar/hangar-watchers.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Essentials').get('/Essentials.json').expectBadge({
+ label: 'watchers',
+ message: isMetric,
+})
+
+t.create('Invalid Resource').get('/does-not-exist.json').expectBadge({
+ label: 'watchers',
+ message: 'not found',
+})
diff --git a/services/hexpm/hexpm.service.js b/services/hexpm/hexpm.service.js
index 457ee9ad9b5be..1f5d5762db239 100644
--- a/services/hexpm/hexpm.service.js
+++ b/services/hexpm/hexpm.service.js
@@ -1,35 +1,27 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric, addv, maybePluralize } = require('../text-formatters')
-const { downloadCount, version: versionColor } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { maybePluralize } from '../text-formatters.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const hexSchema = Joi.object({
downloads: Joi.object({
// these keys may or may not exist
- all: Joi.number()
- .integer()
- .default(0),
- week: Joi.number()
- .integer()
- .default(0),
- day: Joi.number()
- .integer()
- .default(0),
+ all: Joi.number().integer().default(0),
+ week: Joi.number().integer().default(0),
+ day: Joi.number().integer().default(0),
}).required(),
meta: Joi.object({
licenses: Joi.array().required(),
}).required(),
- releases: Joi.array()
- .items(Joi.object({ version: Joi.string().required() }).required())
- .required(),
+ latest_stable_version: Joi.string().allow(null),
+ latest_version: Joi.string().required(),
}).required()
+const description = '[Hex.pm](https://hex.pm/) is a package registry for Erlang'
+
class BaseHexPmService extends BaseJsonService {
- static get defaultBadgeData() {
- return { label: 'hex' }
- }
+ static defaultBadgeData = { label: 'hex' }
async fetch({ packageName }) {
return this._requestJson({
@@ -40,30 +32,27 @@ class BaseHexPmService extends BaseJsonService {
}
class HexPmLicense extends BaseHexPmService {
- static get category() {
- return 'license'
- }
+ static category = 'license'
- static get route() {
- return {
- base: 'hexpm/l',
- pattern: ':packageName',
- }
+ static route = {
+ base: 'hexpm/l',
+ pattern: ':packageName',
}
- static get examples() {
- return [
- {
- title: 'Hex.pm',
- namedParams: { packageName: 'plug' },
- staticPreview: this.render({ licenses: ['Apache 2'] }),
+ static openApi = {
+ '/hexpm/l/{packageName}': {
+ get: {
+ summary: 'Hex.pm License',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'plug',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'license' }
- }
+ static defaultBadgeData = { label: 'license' }
static render({ licenses }) {
if (licenses.length === 0) {
@@ -87,100 +76,91 @@ class HexPmLicense extends BaseHexPmService {
}
class HexPmVersion extends BaseHexPmService {
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: 'hexpm/v',
- pattern: ':packageName',
- }
+ static route = {
+ base: 'hexpm/v',
+ pattern: ':packageName',
}
- static get examples() {
- return [
- {
- title: 'Hex.pm',
- namedParams: { packageName: 'plug' },
- staticPreview: this.render({ version: '1.6.4' }),
+ static openApi = {
+ '/hexpm/v/{packageName}': {
+ get: {
+ summary: 'Hex.pm Version',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'plug',
+ }),
},
- ]
+ },
}
static render({ version }) {
- return { message: addv(version), color: versionColor(version) }
+ return renderVersionBadge({ version })
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
- return this.constructor.render({ version: json.releases[0].version })
+ return this.constructor.render({
+ version: json.latest_stable_version || json.latest_version,
+ })
}
}
-function DownloadsForInterval(interval) {
- const { base, messageSuffix, name } = {
- day: {
- base: 'hexpm/dd',
- messageSuffix: '/day',
- name: 'HexPmDownloadsDay',
- },
- week: {
- base: 'hexpm/dw',
- messageSuffix: '/week',
- name: 'HexPmDownloadsWeek',
- },
- all: {
- base: 'hexpm/dt',
- messageSuffix: '',
- name: 'HexPmDownloadsTotal',
- },
- }[interval]
-
- return class HexPmDownloads extends BaseHexPmService {
- static get name() {
- return name
- }
-
- static get category() {
- return 'downloads'
- }
+const periodMap = {
+ dd: {
+ field: 'day',
+ label: 'day',
+ },
+ dw: {
+ field: 'week',
+ label: 'week',
+ },
+ dt: {
+ field: 'all',
+ },
+}
- static get route() {
- return {
- base,
- pattern: ':packageName',
- }
- }
+class HexPmDownloads extends BaseHexPmService {
+ static category = 'downloads'
- static get examples() {
- return [
- {
- title: 'Hex.pm',
- namedParams: { packageName: 'plug' },
- staticPreview: this.render({ downloads: 85000 }),
- },
- ]
- }
+ static route = {
+ base: 'hexpm',
+ pattern: ':interval(dd|dw|dt)/:packageName',
+ }
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static openApi = {
+ '/hexpm/{interval}/{packageName}': {
+ get: {
+ summary: 'Hex.pm Downloads',
+ description,
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dw',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Weekly, or Total downloads',
+ },
+ {
+ name: 'packageName',
+ example: 'plug',
+ },
+ ),
+ },
+ },
+ }
- static render({ downloads }) {
- return {
- message: `${metric(downloads)}${messageSuffix}`,
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
- async handle({ packageName }) {
- const json = await this.fetch({ packageName })
- return this.constructor.render({ downloads: json.downloads[interval] })
- }
+ async handle({ interval, packageName }) {
+ const json = await this.fetch({ packageName })
+ const downloads = json.downloads[periodMap[interval].field]
+ return renderDownloadsBadge({
+ downloads,
+ interval: periodMap[interval].label,
+ })
}
}
-const downloadsServices = ['day', 'week', 'all'].map(DownloadsForInterval)
-
-module.exports = [...downloadsServices, HexPmLicense, HexPmVersion]
+export default [HexPmDownloads, HexPmLicense, HexPmVersion]
diff --git a/services/hexpm/hexpm.tester.js b/services/hexpm/hexpm.tester.js
index 0ef89e35f11c0..51fd800b9a43a 100644
--- a/services/hexpm/hexpm.tester.js
+++ b/services/hexpm/hexpm.tester.js
@@ -1,12 +1,12 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricOverTimePeriod,
+ isVPlusDottedVersionNClausesWithOptionalSuffix,
+} from '../test-validators.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-
-const isHexpmVersion = Joi.string().regex(/^v\d+.\d+.?\d?$/)
-
-const t = (module.exports = new ServiceTester({ id: 'hexpm', title: 'Hex.pm' }))
+export const t = new ServiceTester({ id: 'hexpm', title: 'Hex.pm' })
t.create('downloads per week')
.get('/dw/cowboy.json')
@@ -23,9 +23,10 @@ t.create('downloads (zero for period)')
.get('/api/packages/cowboy')
.reply(200, {
downloads: { all: 100 }, // there is no 'day' key here
- releases: [{ version: '1.0' }],
+ latest_stable_version: '1.0',
+ latest_version: '1.0',
meta: { licenses: ['MIT'] },
- })
+ }),
)
.expectBadge({ label: 'downloads', message: '0/day' })
@@ -37,21 +38,37 @@ t.create('downloads (not found)')
.get('/dt/this-package-does-not-exist.json')
.expectBadge({ label: 'downloads', message: 'not found' })
-t.create('version')
- .get('/v/cowboy.json')
- .expectBadge({ label: 'hex', message: isHexpmVersion })
+t.create('version').get('/v/cowboy.json').expectBadge({
+ label: 'hex',
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
+})
+
+t.create('version (no stable version)')
+ .get('/v/prima_opentelemetry_ex.json')
+ .intercept(nock =>
+ nock('https://hex.pm/')
+ .get('/api/packages/prima_opentelemetry_ex')
+ .reply(200, {
+ downloads: { all: 100 },
+ latest_stable_version: null,
+ latest_version: '1.0.0-rc.3',
+ meta: { licenses: ['MIT'] },
+ }),
+ )
+ .expectBadge({
+ label: 'hex',
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
+ })
t.create('version (not found)')
.get('/v/this-package-does-not-exist.json')
.expectBadge({ label: 'hex', message: 'not found' })
-t.create('license')
- .get('/l/cowboy.json')
- .expectBadge({
- label: 'license',
- message: Joi.string().required(),
- color: 'blue',
- })
+t.create('license').get('/l/cowboy.json').expectBadge({
+ label: 'license',
+ message: Joi.string().required(),
+ color: 'blue',
+})
t.create('license (multiple licenses)')
.get('/l/cowboy.json')
@@ -60,9 +77,10 @@ t.create('license (multiple licenses)')
.get('/api/packages/cowboy')
.reply(200, {
downloads: { all: 100 },
- releases: [{ version: '1.0' }],
+ latest_stable_version: '1.0',
+ latest_version: '1.0',
meta: { licenses: ['GPLv2', 'MIT'] },
- })
+ }),
)
.expectBadge({
label: 'licenses',
@@ -77,9 +95,10 @@ t.create('license (no license)')
.get('/api/packages/cowboy')
.reply(200, {
downloads: { all: 100 },
- releases: [{ version: '1.0' }],
+ latest_stable_version: '1.0',
+ latest_version: '1.0',
meta: { licenses: [] },
- })
+ }),
)
.expectBadge({
label: 'license',
diff --git a/services/homebrew/homebrew-cask-downloads.service.js b/services/homebrew/homebrew-cask-downloads.service.js
new file mode 100644
index 0000000000000..7b11aea067760
--- /dev/null
+++ b/services/homebrew/homebrew-cask-downloads.service.js
@@ -0,0 +1,82 @@
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+
+function getSchema({ cask }) {
+ return Joi.object({
+ analytics: Joi.object({
+ install: Joi.object({
+ '30d': Joi.object({ [cask]: nonNegativeInteger }).required(),
+ '90d': Joi.object({ [cask]: nonNegativeInteger }).required(),
+ '365d': Joi.object({ [cask]: nonNegativeInteger }).required(),
+ }).required(),
+ }).required(),
+ }).required()
+}
+
+const periodMap = {
+ dm: {
+ api_field: '30d',
+ interval: 'month',
+ },
+ dq: {
+ api_field: '90d',
+ interval: 'quarter',
+ },
+ dy: {
+ api_field: '365d',
+ interval: 'year',
+ },
+}
+
+export default class HomebrewCaskDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'homebrew/cask/installs',
+ pattern: ':interval(dm|dq|dy)/:cask',
+ }
+
+ static openApi = {
+ '/homebrew/cask/installs/{interval}/{cask}': {
+ get: {
+ summary: 'Homebrew Cask Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Monthly, Quarterly or Yearly downloads',
+ },
+ {
+ name: 'cask',
+ example: 'freetube',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async fetch({ cask }) {
+ const schema = getSchema({ cask })
+ return this._requestJson({
+ schema,
+ url: `https://formulae.brew.sh/api/cask/${cask}.json`,
+ httpErrors: { 404: 'cask not found' },
+ })
+ }
+
+ async handle({ interval, cask }) {
+ const {
+ analytics: { install },
+ } = await this.fetch({ cask })
+
+ return renderDownloadsBadge({
+ downloads: install[periodMap[interval].api_field][cask],
+ interval: periodMap[interval].interval,
+ })
+ }
+}
diff --git a/services/homebrew/homebrew-cask-downloads.tester.js b/services/homebrew/homebrew-cask-downloads.tester.js
new file mode 100644
index 0000000000000..57d1ff96369fc
--- /dev/null
+++ b/services/homebrew/homebrew-cask-downloads.tester.js
@@ -0,0 +1,28 @@
+import { createServiceTester } from '../tester.js'
+import { isMetricOverTimePeriod } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('daily downloads (valid)')
+ .get('/dm/freetube.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/dq/freetube.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/dy/freetube.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('daily downloads (not found)')
+ .get('/dm/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'cask not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/dq/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'cask not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/dy/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'cask not found' })
diff --git a/services/homebrew/homebrew-cask-version.service.js b/services/homebrew/homebrew-cask-version.service.js
new file mode 100644
index 0000000000000..8c2e127ae922e
--- /dev/null
+++ b/services/homebrew/homebrew-cask-version.service.js
@@ -0,0 +1,38 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ version: Joi.string().required(),
+}).required()
+
+export default class HomebrewCask extends BaseJsonService {
+ static category = 'version'
+ static route = { base: 'homebrew/cask/v', pattern: ':cask' }
+
+ static openApi = {
+ '/homebrew/cask/v/{cask}': {
+ get: {
+ summary: 'Homebrew Cask Version',
+ parameters: pathParams({
+ name: 'cask',
+ example: 'iterm2',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'homebrew cask' }
+
+ async fetch({ cask }) {
+ return this._requestJson({
+ schema,
+ url: `https://formulae.brew.sh/api/cask/${cask}.json`,
+ })
+ }
+
+ async handle({ cask }) {
+ const data = await this.fetch({ cask })
+ return renderVersionBadge({ version: data.version })
+ }
+}
diff --git a/services/homebrew/homebrew-cask-version.tester.js b/services/homebrew/homebrew-cask-version.tester.js
new file mode 100644
index 0000000000000..2d3918e3716b4
--- /dev/null
+++ b/services/homebrew/homebrew-cask-version.tester.js
@@ -0,0 +1,21 @@
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('homebrew cask (valid)').get('/iterm2.json').expectBadge({
+ label: 'homebrew cask',
+ message: isVPlusTripleDottedVersion,
+})
+
+t.create('homebrew cask (valid)')
+ .get('/iterm2.json')
+ .intercept(nock =>
+ nock('https://formulae.brew.sh')
+ .get('/api/cask/iterm2.json')
+ .reply(200, { version: '3.3.6' }),
+ )
+ .expectBadge({ label: 'homebrew cask', message: 'v3.3.6' })
+
+t.create('homebrew cask (not found)')
+ .get('/not-a-package.json')
+ .expectBadge({ label: 'homebrew cask', message: 'not found' })
diff --git a/services/homebrew/homebrew-cask.service.js b/services/homebrew/homebrew-cask.service.js
deleted file mode 100644
index 7fff101127b85..0000000000000
--- a/services/homebrew/homebrew-cask.service.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- version: Joi.string().required(),
-}).required()
-
-module.exports = class HomebrewCask extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'homebrew/cask/v',
- pattern: ':cask',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'homebrew cask',
- namedParams: { cask: 'iterm2' },
- staticPreview: renderVersionBadge({ version: 'v3.2.5' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'homebrew cask' }
- }
-
- async fetch({ cask }) {
- return this._requestJson({
- schema,
- url: `https://formulae.brew.sh/api/cask/${cask}.json`,
- })
- }
-
- async handle({ cask }) {
- const data = await this.fetch({ cask })
- return renderVersionBadge({ version: data.version })
- }
-}
diff --git a/services/homebrew/homebrew-cask.tester.js b/services/homebrew/homebrew-cask.tester.js
deleted file mode 100644
index 95deb03812edb..0000000000000
--- a/services/homebrew/homebrew-cask.tester.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('homebrew cask (valid)')
- .get('/iterm2.json')
- .expectBadge({
- label: 'homebrew cask',
- message: isVPlusTripleDottedVersion,
- })
-
-t.create('homebrew cask (valid)')
- .get('/iterm2.json')
- .intercept(nock =>
- nock('https://formulae.brew.sh')
- .get('/api/cask/iterm2.json')
- .reply(200, { version: '3.3.6' })
- )
- .expectBadge({ label: 'homebrew cask', message: 'v3.3.6' })
-
-t.create('homebrew cask (not found)')
- .get('/not-a-package.json')
- .expectBadge({ label: 'homebrew cask', message: 'not found' })
diff --git a/services/homebrew/homebrew-formula-downloads.service.js b/services/homebrew/homebrew-formula-downloads.service.js
new file mode 100644
index 0000000000000..37a1e1789079a
--- /dev/null
+++ b/services/homebrew/homebrew-formula-downloads.service.js
@@ -0,0 +1,81 @@
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+
+function getSchema({ formula }) {
+ return Joi.object({
+ analytics: Joi.object({
+ install: Joi.object({
+ '30d': Joi.object({ [formula]: nonNegativeInteger }).required(),
+ '90d': Joi.object({ [formula]: nonNegativeInteger }).required(),
+ '365d': Joi.object({ [formula]: nonNegativeInteger }).required(),
+ }).required(),
+ }).required(),
+ }).required()
+}
+
+const periodMap = {
+ dm: {
+ api_field: '30d',
+ interval: 'month',
+ },
+ dq: {
+ api_field: '90d',
+ interval: 'quarter',
+ },
+ dy: {
+ api_field: '365d',
+ interval: 'year',
+ },
+}
+
+export default class HomebrewDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'homebrew',
+ pattern: 'installs/:interval(dm|dq|dy)/:formula',
+ }
+
+ static openApi = {
+ '/homebrew/installs/{interval}/{formula}': {
+ get: {
+ summary: 'Homebrew Formula Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Monthly, Quarterly or Yearly downloads',
+ },
+ {
+ name: 'formula',
+ example: 'cake',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async fetch({ formula }) {
+ const schema = getSchema({ formula })
+ return this._requestJson({
+ schema,
+ url: `https://formulae.brew.sh/api/formula/${formula}.json`,
+ httpErrors: { 404: 'formula not found' },
+ })
+ }
+
+ async handle({ interval, formula }) {
+ const {
+ analytics: { install },
+ } = await this.fetch({ formula })
+ return renderDownloadsBadge({
+ downloads: install[periodMap[interval].api_field][formula],
+ interval: periodMap[interval].interval,
+ })
+ }
+}
diff --git a/services/homebrew/homebrew-formula-downloads.tester.js b/services/homebrew/homebrew-formula-downloads.tester.js
new file mode 100644
index 0000000000000..972df583f38c9
--- /dev/null
+++ b/services/homebrew/homebrew-formula-downloads.tester.js
@@ -0,0 +1,27 @@
+import { createServiceTester } from '../tester.js'
+import { isMetricOverTimePeriod } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('daily downloads (valid)')
+ .get('/installs/dm/cake.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/installs/dq/cake.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/installs/dy/cake.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('daily downloads (not found)')
+ .get('/installs/dm/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'formula not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/installs/dq/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'formula not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/installs/dy/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'formula not found' })
diff --git a/services/homebrew/homebrew-formula-version.service.js b/services/homebrew/homebrew-formula-version.service.js
new file mode 100644
index 0000000000000..283b645de3c97
--- /dev/null
+++ b/services/homebrew/homebrew-formula-version.service.js
@@ -0,0 +1,41 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ versions: Joi.object({
+ stable: Joi.string().required(),
+ }).required(),
+}).required()
+
+export default class HomebrewVersion extends BaseJsonService {
+ static category = 'version'
+
+ static route = { base: 'homebrew/v', pattern: ':formula' }
+
+ static openApi = {
+ '/homebrew/v/{formula}': {
+ get: {
+ summary: 'Homebrew Formula Version',
+ parameters: pathParams({
+ name: 'formula',
+ example: 'cake',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'homebrew' }
+
+ async fetch({ formula }) {
+ return this._requestJson({
+ schema,
+ url: `https://formulae.brew.sh/api/formula/${formula}.json`,
+ })
+ }
+
+ async handle({ formula }) {
+ const data = await this.fetch({ formula })
+ return renderVersionBadge({ version: data.versions.stable })
+ }
+}
diff --git a/services/homebrew/homebrew-formula-version.tester.js b/services/homebrew/homebrew-formula-version.tester.js
new file mode 100644
index 0000000000000..5441404151795
--- /dev/null
+++ b/services/homebrew/homebrew-formula-version.tester.js
@@ -0,0 +1,21 @@
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('homebrew (valid)').get('/cake.json').expectBadge({
+ label: 'homebrew',
+ message: isVPlusTripleDottedVersion,
+})
+
+t.create('homebrew (valid)')
+ .get('/cake.json')
+ .intercept(nock =>
+ nock('https://formulae.brew.sh')
+ .get('/api/formula/cake.json')
+ .reply(200, { versions: { stable: '0.23.0', devel: null, head: null } }),
+ )
+ .expectBadge({ label: 'homebrew', message: 'v0.23.0' })
+
+t.create('homebrew (not found)')
+ .get('/not-a-package.json')
+ .expectBadge({ label: 'homebrew', message: 'not found' })
diff --git a/services/homebrew/homebrew.service.js b/services/homebrew/homebrew.service.js
deleted file mode 100644
index 7e87c5d56ba7e..0000000000000
--- a/services/homebrew/homebrew.service.js
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- versions: Joi.object({
- stable: Joi.string().required(),
- }).required(),
-}).required()
-
-module.exports = class Homebrew extends BaseJsonService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'homebrew/v',
- pattern: ':formula',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'homebrew',
- namedParams: { formula: 'cake' },
- staticPreview: renderVersionBadge({ version: 'v0.32.0' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'homebrew' }
- }
-
- async fetch({ formula }) {
- return this._requestJson({
- schema,
- url: `https://formulae.brew.sh/api/formula/${formula}.json`,
- })
- }
-
- async handle({ formula }) {
- const data = await this.fetch({ formula })
- return renderVersionBadge({ version: data.versions.stable })
- }
-}
diff --git a/services/homebrew/homebrew.tester.js b/services/homebrew/homebrew.tester.js
deleted file mode 100644
index 0abbddfa94b36..0000000000000
--- a/services/homebrew/homebrew.tester.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('homebrew (valid)')
- .get('/cake.json')
- .expectBadge({
- label: 'homebrew',
- message: isVPlusTripleDottedVersion,
- })
-
-t.create('homebrew (valid)')
- .get('/cake.json')
- .intercept(nock =>
- nock('https://formulae.brew.sh')
- .get('/api/formula/cake.json')
- .reply(200, { versions: { stable: '0.23.0', devel: null, head: null } })
- )
- .expectBadge({ label: 'homebrew', message: 'v0.23.0' })
-
-t.create('homebrew (not found)')
- .get('/not-a-package.json')
- .expectBadge({ label: 'homebrew', message: 'not found' })
diff --git a/services/hsts/hsts.service.js b/services/hsts/hsts.service.js
index 707078ef0e7de..f809145f595e2 100644
--- a/services/hsts/hsts.service.js
+++ b/services/hsts/hsts.service.js
@@ -1,48 +1,41 @@
-'use strict'
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
const label = 'hsts preloaded'
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
const schema = Joi.object({
status: Joi.string().required(),
}).required()
-const documentation = `
-
-
- Strict-Transport-Security is an HTTP response header that signals that browsers should
- only access the site using HTTPS.
-
-
- For a higher level of security, it's possible for a domain owner to
- preload
- this behavior into participating web browsers . Chromium maintains the HSTS preload list , which
- is the de facto standard that has been adopted by several browsers. This service checks a domain's status in that list.
-
+const description = `
+[\`Strict-Transport-Security\` is an HTTP response header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
+that signals that browsers should only access the site using HTTPS.
+
+For a higher level of security, it's possible for a domain owner to
+[preload this behavior into participating web browsers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security).
+Chromium maintains the [HSTS preload list](https://www.chromium.org/hsts), which
+is the de facto standard that has been adopted by several browsers.
+This service checks a domain's status in that list.
`
-module.exports = class HSTS extends BaseJsonService {
- static get category() {
- return 'monitoring'
- }
+export default class HSTS extends BaseJsonService {
+ static category = 'monitoring'
- static get route() {
- return {
- base: 'hsts/preload',
- pattern: ':domain',
- }
+ static route = {
+ base: 'hsts/preload',
+ pattern: ':domain',
}
- static get examples() {
- return [
- {
- title: 'Chromium HSTS preload',
- namedParams: { domain: 'github.com' },
- staticPreview: this.render({ status: 'preloaded' }),
- keywords: ['security'],
- documentation,
+ static openApi = {
+ '/hsts/preload/{domain}': {
+ get: {
+ summary: 'Chromium HSTS preload',
+ description,
+ parameters: pathParams({
+ name: 'domain',
+ example: 'github.com',
+ }),
},
- ]
+ },
}
static render({ status }) {
@@ -63,8 +56,8 @@ module.exports = class HSTS extends BaseJsonService {
async fetch({ domain }) {
return this._requestJson({
schema,
- url: `https://hstspreload.org/api/v2/status`,
- options: { qs: { domain } },
+ url: 'https://hstspreload.org/api/v2/status',
+ options: { searchParams: { domain } },
})
}
diff --git a/services/hsts/hsts.tester.js b/services/hsts/hsts.tester.js
index fffc9b1a62a36..215e3d3379c93 100644
--- a/services/hsts/hsts.tester.js
+++ b/services/hsts/hsts.tester.js
@@ -1,15 +1,12 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const label = 'hsts preloaded'
-t.create('gets the hsts status of github')
- .get('/github.com.json')
- .expectBadge({
- label,
- message: 'yes',
- color: 'brightgreen',
- })
+t.create('gets the hsts status of github').get('/github.com.json').expectBadge({
+ label,
+ message: 'yes',
+ color: 'brightgreen',
+})
t.create('gets the hsts status of httpforever')
.get('/httpforever.com.json')
@@ -32,7 +29,7 @@ t.create('gets the hsts status of github (mock)')
.intercept(nock =>
nock('https://hstspreload.org')
.get('/api/v2/status?domain=github.com')
- .reply(200, { status: 'preloaded' })
+ .reply(200, { status: 'preloaded' }),
)
.expectBadge({
label,
@@ -45,7 +42,7 @@ t.create('gets the hsts status of httpforever (mock)')
.intercept(nock =>
nock('https://hstspreload.org')
.get('/api/v2/status?domain=httpforever.com')
- .reply(200, { status: 'unknown' })
+ .reply(200, { status: 'unknown' }),
)
.expectBadge({
label,
@@ -58,7 +55,7 @@ t.create('gets the hsts status of a pending site (mock)')
.intercept(nock =>
nock('https://hstspreload.org')
.get('/api/v2/status?domain=pending.mock')
- .reply(200, { status: 'pending' })
+ .reply(200, { status: 'pending' }),
)
.expectBadge({
label,
@@ -71,7 +68,7 @@ t.create('gets the status of an invalid uri (mock)')
.intercept(nock =>
nock('https://hstspreload.org')
.get('/api/v2/status?domain=does-not-exist')
- .reply(200, { status: 'unknown' })
+ .reply(200, { status: 'unknown' }),
)
.expectBadge({
label,
diff --git a/services/imagelayers/imagelayers.service.js b/services/imagelayers/imagelayers.service.js
deleted file mode 100644
index 8e948c17ab435..0000000000000
--- a/services/imagelayers/imagelayers.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'size',
- route: {
- base: 'imagelayers',
- pattern: ':various+',
- },
- label: 'imagelayers',
- dateAdded: new Date('2018-11-18'),
-})
diff --git a/services/imagelayers/imagelayers.tester.js b/services/imagelayers/imagelayers.tester.js
deleted file mode 100644
index 35234ace9ed26..0000000000000
--- a/services/imagelayers/imagelayers.tester.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'imagelayers',
- title: 'ImageLayers',
-}))
-
-t.create('no longer available (previously image size)')
- .get('/image-size/_/ubuntu/latest.json')
- .expectBadge({
- label: 'imagelayers',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously number of layers)')
- .get('/layers/_/ubuntu/latest.json')
- .expectBadge({
- label: 'imagelayers',
- message: 'no longer available',
- })
diff --git a/services/index.js b/services/index.js
index 14d418b6df2fe..1ba849b1b8f42 100644
--- a/services/index.js
+++ b/services/index.js
@@ -1,7 +1 @@
-'use strict'
-
-const base = require('../core/base-service')
-
-module.exports = {
- ...base,
-}
+export * from '../core/base-service/index.js'
diff --git a/services/issuestats/issuestats.service.js b/services/issuestats/issuestats.service.js
deleted file mode 100644
index 6b22c1d3f81a3..0000000000000
--- a/services/issuestats/issuestats.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'issue-tracking',
- route: {
- base: 'issuestats',
- format: '(?:.*?)',
- },
- label: 'issue stats',
- dateAdded: new Date('2018-09-01'),
-})
diff --git a/services/issuestats/issuestats.tester.js b/services/issuestats/issuestats.tester.js
deleted file mode 100644
index 1eab93c18a6c5..0000000000000
--- a/services/issuestats/issuestats.tester.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({ id: 'issuestats', title: 'Issue Stats' })
-module.exports = t
-
-t.create('no longer available (previously issue analysis)')
- .get('/i/github/expressjs/express.json')
- .expectBadge({
- label: 'issue stats',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously pull request analysis, long form)')
- .get('/p/long/github/expressjs/express.json')
- .expectBadge({
- label: 'issue stats',
- message: 'no longer available',
- })
diff --git a/services/itunes/itunes.service.js b/services/itunes/itunes.service.js
index a460c8a93082a..541ba5a8f988f 100644
--- a/services/itunes/itunes.service.js
+++ b/services/itunes/itunes.service.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
const schema = Joi.object({
resultCount: nonNegativeInteger,
@@ -12,31 +10,27 @@ const schema = Joi.object({
.min(0),
}).required()
-module.exports = class Itunes extends BaseJsonService {
- static get category() {
- return 'version'
- }
+export default class Itunes extends BaseJsonService {
+ static category = 'version'
- static get route() {
- return {
- base: 'itunes/v',
- pattern: ':bundleId',
- }
+ static route = {
+ base: 'itunes/v',
+ pattern: ':bundleId',
}
- static get examples() {
- return [
- {
- title: 'iTunes App Store',
- namedParams: { bundleId: '803453959' },
- staticPreview: renderVersionBadge({ version: 'v3.3.3' }),
+ static openApi = {
+ '/itunes/v/{bundleId}': {
+ get: {
+ summary: 'iTunes App Store',
+ parameters: pathParams({
+ name: 'bundleId',
+ example: '803453959',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'itunes app store' }
- }
+ static defaultBadgeData = { label: 'itunes app store' }
async fetch({ bundleId }) {
return this._requestJson({
diff --git a/services/itunes/itunes.tester.js b/services/itunes/itunes.tester.js
index 54a5a8b568f5e..2499ed3f9702d 100644
--- a/services/itunes/itunes.tester.js
+++ b/services/itunes/itunes.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('iTunes version (valid)')
- .get('/324684580.json')
- .expectBadge({
- label: 'itunes app store',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('iTunes version (valid)').get('/324684580.json').expectBadge({
+ label: 'itunes app store',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('iTunes version (not found)')
.get('/9.json')
diff --git a/services/jenkins/jenkins-base.js b/services/jenkins/jenkins-base.js
index c2d1e5b3b0c36..942c3bf567acd 100644
--- a/services/jenkins/jenkins-base.js
+++ b/services/jenkins/jenkins-base.js
@@ -1,33 +1,25 @@
-'use strict'
+import { BaseJsonService } from '../index.js'
-const { BaseJsonService } = require('..')
-
-module.exports = class JenkinsBase extends BaseJsonService {
- static get auth() {
- return {
- userKey: 'jenkins_user',
- passKey: 'jenkins_pass',
- serviceKey: 'jenkins',
- }
+export default class JenkinsBase extends BaseJsonService {
+ static auth = {
+ userKey: 'jenkins_user',
+ passKey: 'jenkins_pass',
+ serviceKey: 'jenkins',
}
async fetch({
url,
schema,
- qs,
- errorMessages = { 404: 'instance or job not found' },
- disableStrictSSL,
+ searchParams,
+ httpErrors = { 404: 'instance or job not found' },
}) {
return this._requestJson(
this.authHelper.withBasicAuth({
url,
- options: {
- qs,
- strictSSL: disableStrictSSL === undefined,
- },
+ options: { searchParams },
schema,
- errorMessages,
- })
+ httpErrors,
+ }),
)
}
}
diff --git a/services/jenkins/jenkins-build-redirect.service.js b/services/jenkins/jenkins-build-redirect.service.js
index b26738b726d15..6c9a9939e585b 100644
--- a/services/jenkins/jenkins-build-redirect.service.js
+++ b/services/jenkins/jenkins-build-redirect.service.js
@@ -1,39 +1,32 @@
-'use strict'
-
-const { redirector } = require('..')
-const { buildRedirectUrl } = require('./jenkins-common')
+import { retiredService } from '../index.js'
const commonProps = {
category: 'build',
- transformPath: () => '/jenkins/build',
- transformQueryParams: ({ protocol, host, job }) => ({
- jobUrl: buildRedirectUrl({ protocol, host, job }),
- }),
+ label: 'jenkins',
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}
-module.exports = [
- redirector({
+export default [
+ retiredService({
route: {
base: 'jenkins-ci/s',
pattern: ':protocol(http|https)/:host/:job+',
},
- dateAdded: new Date('2019-04-20'),
...commonProps,
}),
- redirector({
+ retiredService({
route: {
base: 'jenkins/s',
pattern: ':protocol(http|https)/:host/:job+',
},
- dateAdded: new Date('2019-04-20'),
...commonProps,
}),
- redirector({
+ retiredService({
route: {
base: 'jenkins/build',
pattern: ':protocol(http|https)/:host/:job+',
},
- dateAdded: new Date('2019-11-29'),
...commonProps,
}),
]
diff --git a/services/jenkins/jenkins-build-redirect.tester.js b/services/jenkins/jenkins-build-redirect.tester.js
index 33a0168837a1a..dd421d4f0b5c5 100644
--- a/services/jenkins/jenkins-build-redirect.tester.js
+++ b/services/jenkins/jenkins-build-redirect.tester.js
@@ -1,33 +1,28 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JenkinsBuildRedirect',
title: 'JenkinsBuildRedirect',
pathPrefix: '/',
-}))
+})
t.create('old jenkins ci prefix + job url in path')
- .get('jenkins-ci/s/https/updates.jenkins-ci.org/job/foo.svg')
- .expectRedirect(
- `/jenkins/build.svg?jobUrl=${encodeURIComponent(
- 'https://updates.jenkins-ci.org/job/foo'
- )}`
- )
+ .get('jenkins-ci/s/https/updates.jenkins-ci.org/job/foo.json')
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('old jenkins shorthand prefix + job url in path')
- .get('jenkins/s/https/updates.jenkins-ci.org/job/foo.svg')
- .expectRedirect(
- `/jenkins/build.svg?jobUrl=${encodeURIComponent(
- 'https://updates.jenkins-ci.org/job/foo'
- )}`
- )
+ .get('jenkins/s/https/updates.jenkins-ci.org/job/foo.json')
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('new jenkins build prefix + job url in path')
- .get('jenkins/build/https/updates.jenkins-ci.org/job/foo.svg')
- .expectRedirect(
- `/jenkins/build.svg?jobUrl=${encodeURIComponent(
- 'https://updates.jenkins-ci.org/job/foo'
- )}`
- )
+ .get('jenkins/build/https/updates.jenkins-ci.org/job/foo.json')
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/jenkins/jenkins-build.service.js b/services/jenkins/jenkins-build.service.js
index fab88a72f87da..49ca970508819 100644
--- a/services/jenkins/jenkins-build.service.js
+++ b/services/jenkins/jenkins-build.service.js
@@ -1,13 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderBuildStatusBadge } = require('../build-status')
-const JenkinsBase = require('./jenkins-base')
-const {
+import Joi from 'joi'
+import { queryParam } from '../index.js'
+import { renderBuildStatusBadge } from '../build-status.js'
+import JenkinsBase from './jenkins-base.js'
+import {
buildTreeParamQueryString,
buildUrl,
queryParamSchema,
-} = require('./jenkins-common')
+} from './jenkins-common.js'
// https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/BallColor.java#L56
const colorStatusMap = {
@@ -30,40 +29,34 @@ const colorStatusMap = {
}
const schema = Joi.object({
- color: Joi.allow(...Object.keys(colorStatusMap)).required(),
+ color: Joi.equal(...Object.keys(colorStatusMap)).required(),
}).required()
-module.exports = class JenkinsBuild extends JenkinsBase {
- static get category() {
- return 'build'
- }
+export default class JenkinsBuild extends JenkinsBase {
+ static category = 'build'
- static get route() {
- return {
- base: 'jenkins',
- pattern: 'build',
- queryParamSchema,
- }
+ static route = {
+ base: 'jenkins',
+ pattern: 'build',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Jenkins',
- namedParams: {},
- queryParams: {
- jobUrl: 'https://wso2.org/jenkins/view/All%20Builds/job/archetypes',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+ static openApi = {
+ '/jenkins/build': {
+ get: {
+ summary: 'Jenkins Build',
+ parameters: [
+ queryParam({
+ name: 'jobUrl',
+ example: 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
- }
+ static defaultBadgeData = { label: 'build' }
static render({ status }) {
if (status === 'unstable') {
@@ -80,12 +73,11 @@ module.exports = class JenkinsBuild extends JenkinsBase {
return { status: colorStatusMap[json.color] }
}
- async handle(namedParams, { jobUrl, disableStrictSSL }) {
+ async handle(namedParams, { jobUrl }) {
const json = await this.fetch({
url: buildUrl({ jobUrl, lastCompletedBuild: false }),
schema,
- qs: buildTreeParamQueryString('color'),
- disableStrictSSL,
+ searchParams: buildTreeParamQueryString('color'),
})
const { status } = this.transform({ json })
return this.constructor.render({ status })
diff --git a/services/jenkins/jenkins-build.spec.js b/services/jenkins/jenkins-build.spec.js
index 411436a8a671c..20f5c58914fe8 100644
--- a/services/jenkins/jenkins-build.spec.js
+++ b/services/jenkins/jenkins-build.spec.js
@@ -1,13 +1,19 @@
-'use strict'
+import { test, forCases, given } from 'sazerac'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { testAuth } from '../test-helpers.js'
+import JenkinsBuild from './jenkins-build.service.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { test, forCases, given } = require('sazerac')
-const { renderBuildStatusBadge } = require('../build-status')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const JenkinsBuild = require('./jenkins-build.service')
+const authConfigOverride = {
+ public: {
+ services: {
+ jenkins: {
+ authorizedOrigins: ['https://ci.freebsd.org'],
+ },
+ },
+ },
+}
-describe('JenkinsBuild', function() {
+describe('JenkinsBuild', function () {
test(JenkinsBuild.prototype.transform, () => {
forCases([
given({ json: { color: 'red_anime' } }),
@@ -45,63 +51,27 @@ describe('JenkinsBuild', function() {
color: 'yellow',
})
given({ status: 'passing' }).expect(
- renderBuildStatusBadge({ status: 'passing' })
+ renderBuildStatusBadge({ status: 'passing' }),
)
given({ status: 'failing' }).expect(
- renderBuildStatusBadge({ status: 'failing' })
+ renderBuildStatusBadge({ status: 'failing' }),
)
given({ status: 'building' }).expect(
- renderBuildStatusBadge({ status: 'building' })
+ renderBuildStatusBadge({ status: 'building' }),
)
given({ status: 'not built' }).expect(
- renderBuildStatusBadge({ status: 'not built' })
+ renderBuildStatusBadge({ status: 'not built' }),
)
})
- describe('auth', function() {
- cleanUpNockAfterEach()
-
- const user = 'admin'
- const pass = 'password'
- const config = {
- public: {
- services: {
- jenkins: {
- authorizedOrigins: ['https://jenkins.ubuntu.com'],
- },
- },
- },
- private: {
- jenkins_user: user,
- jenkins_pass: pass,
- },
- }
-
- it('sends the auth information as configured', async function() {
- const scope = nock('https://jenkins.ubuntu.com')
- .get('/server/job/curtin-vmtest-daily-x/api/json?tree=color')
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, { color: 'blue' })
-
- expect(
- await JenkinsBuild.invoke(
- defaultContext,
- config,
- {},
- {
- jobUrl:
- 'https://jenkins.ubuntu.com/server/job/curtin-vmtest-daily-x',
- }
- )
- ).to.deep.equal({
- label: undefined,
- message: 'passing',
- color: 'brightgreen',
- })
-
- scope.done()
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ JenkinsBuild,
+ 'BasicAuth',
+ { color: 'blue' },
+ { configOverride: authConfigOverride },
+ )
})
})
})
diff --git a/services/jenkins/jenkins-build.tester.js b/services/jenkins/jenkins-build.tester.js
index 7de9c6cdb1511..09bdc7525d820 100644
--- a/services/jenkins/jenkins-build.tester.js
+++ b/services/jenkins/jenkins-build.tester.js
@@ -1,26 +1,17 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isJenkinsBuildStatus = Joi.alternatives(
- isBuildStatus,
- Joi.string().allow('unstable')
-)
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('build job not found')
- .get('/build.json?jobUrl=https://ci.eclipse.org/jgit/job/does-not-exist')
+ .get('/build.json?jobUrl=https://ci.freebsd.org/job/does-not-exist')
.expectBadge({ label: 'build', message: 'instance or job not found' })
t.create('build found (view)')
.get(
- `/build.json?jobUrl=${encodeURIComponent(
- 'https://wso2.org/jenkins/view/All Builds/job/archetypes'
- )}`
+ '/build.json?jobUrl=https://ci.freebsd.org/view/all/job/FreeBSD-main-amd64-test',
)
- .expectBadge({ label: 'build', message: isJenkinsBuildStatus })
+ .expectBadge({ label: 'build', message: isBuildStatus })
t.create('build found (job)')
- .get('/build.json?jobUrl=https://ci.eclipse.org/jgit/job/jgit')
- .expectBadge({ label: 'build', message: isJenkinsBuildStatus })
+ .get('/build.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-test')
+ .expectBadge({ label: 'build', message: isBuildStatus })
diff --git a/services/jenkins/jenkins-common.js b/services/jenkins/jenkins-common.js
index c555abd746b92..7fd5dd179d973 100644
--- a/services/jenkins/jenkins-common.js
+++ b/services/jenkins/jenkins-common.js
@@ -1,12 +1,7 @@
-'use strict'
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-
-const queryParamSchema = Joi.object({
- disableStrictSSL: Joi.equal(''),
- jobUrl: optionalUrl,
-}).required()
+const queryParamSchema = Joi.object({ jobUrl: optionalUrl }).required()
const buildRedirectUrl = ({ protocol, host, job }) => {
const jobPrefix = job.indexOf('/') > -1 ? '' : 'job/'
@@ -21,9 +16,5 @@ const buildUrl = ({ jobUrl, lastCompletedBuild = true, plugin }) => {
return `${jobUrl}/${lastCompletedBuildElement}${pluginElement}api/json`
}
-module.exports = {
- queryParamSchema,
- buildTreeParamQueryString: tree => ({ tree }),
- buildUrl,
- buildRedirectUrl,
-}
+export { queryParamSchema, buildUrl, buildRedirectUrl }
+export const buildTreeParamQueryString = tree => ({ tree })
diff --git a/services/jenkins/jenkins-common.spec.js b/services/jenkins/jenkins-common.spec.js
index 914690336cb3a..8589ca9695a8c 100644
--- a/services/jenkins/jenkins-common.spec.js
+++ b/services/jenkins/jenkins-common.spec.js
@@ -1,44 +1,43 @@
-'use strict'
-const { expect } = require('chai')
-const { buildRedirectUrl, buildUrl } = require('./jenkins-common')
+import { expect } from 'chai'
+import { buildRedirectUrl, buildUrl } from './jenkins-common.js'
-describe('jenkins-common', function() {
- describe('buildUrl', function() {
- it('returns the json api url', function() {
+describe('jenkins-common', function () {
+ describe('buildUrl', function () {
+ it('returns the json api url', function () {
const actualResult = buildUrl({
- jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
+ jobUrl: 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
})
expect(actualResult).to.equal(
- 'https://ci.eclipse.org/jgit/job/jgit/lastCompletedBuild/api/json'
+ 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test/lastCompletedBuild/api/json',
)
})
- it('returns the json api url including a plugin name', function() {
+ it('returns the json api url including a plugin name', function () {
const actualResult = buildUrl({
- jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
+ jobUrl: 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
plugin: 'cobertura',
})
expect(actualResult).to.equal(
- 'https://ci.eclipse.org/jgit/job/jgit/lastCompletedBuild/cobertura/api/json'
+ 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test/lastCompletedBuild/cobertura/api/json',
)
})
- it('returns the json api url without the lastCompletedBuild element', function() {
+ it('returns the json api url without the lastCompletedBuild element', function () {
const actualResult = buildUrl({
- jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
+ jobUrl: 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
lastCompletedBuild: false,
})
expect(actualResult).to.equal(
- 'https://ci.eclipse.org/jgit/job/jgit/api/json'
+ 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test/api/json',
)
})
})
- describe('buildRedirectUrl', function() {
- it('returns the job url', function() {
+ describe('buildRedirectUrl', function () {
+ it('returns the job url', function () {
const actualResult = buildRedirectUrl({
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
@@ -46,11 +45,11 @@ describe('jenkins-common', function() {
})
expect(actualResult).to.equal(
- 'https://jenkins.sqlalchemy.org/job/alembic_coverage'
+ 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
)
})
- it('returns the job url and adds missing /job prefixes', function() {
+ it('returns the job url and adds missing /job prefixes', function () {
const actualResult = buildRedirectUrl({
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
@@ -58,7 +57,7 @@ describe('jenkins-common', function() {
})
expect(actualResult).to.equal(
- 'https://jenkins.sqlalchemy.org/job/alembic_coverage'
+ 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
)
})
})
diff --git a/services/jenkins/jenkins-coverage-redirector.service.js b/services/jenkins/jenkins-coverage-redirector.service.js
index 7658977c15c3b..62ffb3dede022 100644
--- a/services/jenkins/jenkins-coverage-redirector.service.js
+++ b/services/jenkins/jenkins-coverage-redirector.service.js
@@ -1,35 +1,42 @@
-'use strict'
-
-const { redirector } = require('..')
-const { buildRedirectUrl } = require('./jenkins-common')
+import { redirector, retiredService } from '../index.js'
const commonProps = {
category: 'coverage',
- transformQueryParams: ({ protocol, host, job }) => ({
- jobUrl: buildRedirectUrl({ protocol, host, job }),
- }),
+ label: 'jenkins',
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}
-module.exports = [
- redirector({
+export default [
+ retiredService({
route: {
base: 'jenkins',
pattern: ':coverageFormat(j|c)/:protocol(http|https)/:host/:job+',
},
- transformPath: ({ coverageFormat }) =>
- `/jenkins/coverage/${coverageFormat === 'j' ? 'jacoco' : 'cobertura'}`,
- dateAdded: new Date('2019-04-20'),
...commonProps,
}),
- redirector({
+ retiredService({
route: {
base: 'jenkins/coverage',
pattern:
':coverageFormat(jacoco|cobertura|api)/:protocol(http|https)/:host/:job+',
},
- transformPath: ({ coverageFormat }) =>
- `/jenkins/coverage/${coverageFormat}`,
- dateAdded: new Date('2019-11-29'),
...commonProps,
}),
+ retiredService({
+ route: {
+ base: 'jenkins/coverage/api',
+ pattern: '',
+ },
+ ...commonProps,
+ }),
+ redirector({
+ category: 'coverage',
+ route: {
+ base: 'jenkins/coverage',
+ pattern: ':format(jacoco|cobertura|apiv1|apiv4)',
+ },
+ transformPath: () => '/jenkins/coverage',
+ dateAdded: new Date('2026-05-17'),
+ }),
]
diff --git a/services/jenkins/jenkins-coverage-redirector.tester.js b/services/jenkins/jenkins-coverage-redirector.tester.js
index fd67cf389da05..43c2af07f12fc 100644
--- a/services/jenkins/jenkins-coverage-redirector.tester.js
+++ b/services/jenkins/jenkins-coverage-redirector.tester.js
@@ -1,57 +1,91 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JenkinsCoverageRedirector',
title: 'JenkinsCoverageRedirector',
pathPrefix: '/jenkins',
-}))
+})
t.create('old Jacoco prefix + job url in path')
.get(
- '/j/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.svg'
- )
- .expectRedirect(
- `/jenkins/coverage/jacoco.svg?jobUrl=${encodeURIComponent(
- 'https://wso2.org/jenkins/view/All Builds/job/sonar/job/sonar-carbon-dashboards'
- )}`
+ '/j/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.json',
)
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('new Jacoco prefix + job url in path')
.get(
- '/coverage/jacoco/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.svg'
+ '/coverage/jacoco/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.json',
+ )
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('old Cobertura prefix + job url in path')
+ .get('/c/https/jenkins.sqlalchemy.org/job/alembic_coverage.json')
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('new Cobertura prefix + job url in path')
+ .get(
+ '/coverage/cobertura/https/jenkins.sqlalchemy.org/job/alembic_coverage.json',
+ )
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('api prefix + job url in path')
+ .get(
+ '/coverage/api/https/jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master.json',
+ )
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('old v1 api prefix to new prefix')
+ .get(
+ '/coverage/api.json?jobUrl=http://loneraver.duckdns.org:8082/job/github/job/VisVid/job/master',
+ )
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
+
+t.create('jacoco format redirects to unique route')
+ .get(
+ `/coverage/jacoco.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
.expectRedirect(
- `/jenkins/coverage/jacoco.svg?jobUrl=${encodeURIComponent(
- 'https://wso2.org/jenkins/view/All Builds/job/sonar/job/sonar-carbon-dashboards'
- )}`
+ `/jenkins/coverage.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
-t.create('old Cobertura prefix + job url in path')
- .get('/c/https/jenkins.sqlalchemy.org/job/alembic_coverage.svg')
+t.create('cobertura format redirects to unique route')
+ .get(
+ `/coverage/cobertura.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
+ )
.expectRedirect(
- `/jenkins/coverage/cobertura.svg?jobUrl=${encodeURIComponent(
- 'https://jenkins.sqlalchemy.org/job/alembic_coverage'
- )}`
+ `/jenkins/coverage.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
-t.create('new Cobertura prefix + job url in path')
+t.create('apiv1 format redirects to unique route')
.get(
- '/coverage/cobertura/https/jenkins.sqlalchemy.org/job/alembic_coverage.svg'
+ `/coverage/apiv1.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
.expectRedirect(
- `/jenkins/coverage/cobertura.svg?jobUrl=${encodeURIComponent(
- 'https://jenkins.sqlalchemy.org/job/alembic_coverage'
- )}`
+ `/jenkins/coverage.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
-t.create('api prefix + job url in path')
+t.create('apiv4 format redirects to unique route')
.get(
- '/coverage/api/https/jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master.svg'
+ `/coverage/apiv4.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
.expectRedirect(
- `/jenkins/coverage/api.svg?jobUrl=${encodeURIComponent(
- 'https://jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master'
- )}`
+ `/jenkins/coverage.svg?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master`,
)
diff --git a/services/jenkins/jenkins-coverage.service.js b/services/jenkins/jenkins-coverage.service.js
index be31e4b18365a..c46486ff624ec 100644
--- a/services/jenkins/jenkins-coverage.service.js
+++ b/services/jenkins/jenkins-coverage.service.js
@@ -1,128 +1,50 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage } = require('../color-formatters')
-const JenkinsBase = require('./jenkins-base')
-const {
+import Joi from 'joi'
+import { queryParam } from '../index.js'
+import { coveragePercentage } from '../color-formatters.js'
+import JenkinsBase from './jenkins-base.js'
+import {
buildTreeParamQueryString,
buildUrl,
queryParamSchema,
-} = require('./jenkins-common')
+} from './jenkins-common.js'
-const formatMap = {
- jacoco: {
- schema: Joi.object({
- instructionCoverage: Joi.object({
- percentage: Joi.number()
- .min(0)
- .max(100)
- .required(),
- }).required(),
- }).required(),
- treeQueryParam: 'instructionCoverage[percentage]',
- transform: json => ({ coverage: json.instructionCoverage.percentage }),
- pluginSpecificPath: 'jacoco',
- },
- cobertura: {
- schema: Joi.object({
- results: Joi.object({
- elements: Joi.array()
- .items(
- Joi.object({
- name: Joi.string().required(),
- ratio: Joi.number()
- .min(0)
- .max(100)
- .required(),
- })
- )
- .has(Joi.object({ name: 'Lines' }))
- .min(1)
- .required(),
- }).required(),
- }).required(),
- treeQueryParam: 'results[elements[name,ratio]]',
- transform: json => {
- const lineCoverage = json.results.elements.find(
- element => element.name === 'Lines'
- )
- return { coverage: lineCoverage.ratio }
- },
- pluginSpecificPath: 'cobertura',
- },
- api: {
- schema: Joi.object({
- results: Joi.object({
- elements: Joi.array()
- .items(
- Joi.object({
- name: Joi.string().required(),
- ratio: Joi.number()
- .min(0)
- .max(100)
- .required(),
- })
- )
- .has(Joi.object({ name: 'Line' }))
- .min(1)
- .required(),
- }).required(),
- }).required(),
- treeQueryParam: 'results[elements[name,ratio]]',
- transform: json => {
- const lineCoverage = json.results.elements.find(
- element => element.name === 'Line'
- )
- return { coverage: lineCoverage.ratio }
- },
- pluginSpecificPath: 'coverage/result',
- },
-}
+const schemaCoverage = Joi.object({
+ projectStatistics: Joi.object({
+ line: Joi.string()
+ .pattern(/\d+\.\d+%/)
+ .required(),
+ }).required(),
+}).required()
-const documentation = `
-
- We support coverage metrics from a variety of Jenkins plugins:
-
-
-`
+const description =
+ 'We support coverage metrics from the Jenkins Coverage Plugin .'
-module.exports = class JenkinsCoverage extends JenkinsBase {
- static get category() {
- return 'coverage'
- }
+export default class JenkinsCoverage extends JenkinsBase {
+ static category = 'coverage'
- static get route() {
- return {
- base: 'jenkins/coverage',
- pattern: ':format(jacoco|cobertura|api)',
- queryParamSchema,
- }
+ static route = {
+ base: 'jenkins/coverage',
+ pattern: '',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Jenkins Coverage',
- namedParams: {
- format: 'cobertura',
- },
- queryParams: {
- jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
- },
- keywords: ['jacoco', 'cobertura', 'llvm-cov', 'istanbul'],
- staticPreview: this.render({ coverage: 95 }),
- documentation,
+ static openApi = {
+ '/jenkins/coverage': {
+ get: {
+ summary: 'Jenkins Coverage',
+ description,
+ parameters: [
+ queryParam({
+ name: 'jobUrl',
+ example: 'https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'coverage' }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ coverage }) {
return {
@@ -131,20 +53,23 @@ module.exports = class JenkinsCoverage extends JenkinsBase {
}
}
- async handle({ format }, { jobUrl, disableStrictSSL }) {
- const { schema, transform, treeQueryParam, pluginSpecificPath } = formatMap[
- format
- ]
+ async handle(_routeParams, { jobUrl }) {
const json = await this.fetch({
- url: buildUrl({ jobUrl, plugin: pluginSpecificPath }),
- schema,
- qs: buildTreeParamQueryString(treeQueryParam),
- disableStrictSSL,
- errorMessages: {
+ url: buildUrl({ jobUrl, plugin: 'coverage' }),
+ schema: schemaCoverage,
+ searchParams: buildTreeParamQueryString('projectStatistics[line]'),
+ httpErrors: {
404: 'job or coverage not found',
},
})
- const { coverage } = transform(json)
- return this.constructor.render({ coverage })
+ const lineCoverageStr = json.projectStatistics.line
+ const lineCoverage = lineCoverageStr.substring(
+ 0,
+ lineCoverageStr.length - 1,
+ )
+
+ return this.constructor.render({
+ coverage: Number.parseFloat(lineCoverage),
+ })
}
}
diff --git a/services/jenkins/jenkins-coverage.spec.js b/services/jenkins/jenkins-coverage.spec.js
new file mode 100644
index 0000000000000..ffa9e9e5ff76f
--- /dev/null
+++ b/services/jenkins/jenkins-coverage.spec.js
@@ -0,0 +1,25 @@
+import { testAuth } from '../test-helpers.js'
+import JenkinsCoverage from './jenkins-coverage.service.js'
+
+const authConfigOverride = {
+ public: {
+ services: {
+ jenkins: {
+ authorizedOrigins: ['https://jenkins.mm12.xyz'],
+ },
+ },
+ },
+}
+
+describe('JenkinsCoverage', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ JenkinsCoverage,
+ 'BasicAuth',
+ { projectStatistics: { line: '93.0%' } },
+ { configOverride: authConfigOverride },
+ )
+ })
+ })
+})
diff --git a/services/jenkins/jenkins-coverage.tester.js b/services/jenkins/jenkins-coverage.tester.js
index 525187cd58565..0e61e50fa81fc 100644
--- a/services/jenkins/jenkins-coverage.tester.js
+++ b/services/jenkins/jenkins-coverage.tester.js
@@ -1,45 +1,11 @@
-'use strict'
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-// The below page includes links to various publicly accessible Jenkins instances
-// although many of the links are dead, it is is still a helpful resource for finding
-// target Jenkins instances/jobs to use for testing.
-// https://wiki.jenkins.io/pages/viewpage.action?pageId=58001258
-
-t.create('jacoco: job found')
- .get(
- `/jacoco.json?jobUrl=${encodeURIComponent(
- 'https://wso2.org/jenkins/view/All%20Builds/job/archetypes'
- )}`
- )
- .expectBadge({ label: 'coverage', message: isIntegerPercentage })
-
-t.create('jacoco: job not found')
- .get('/jacoco.json?jobUrl=https://wso2.org/jenkins/job/does-not-exist')
- .expectBadge({ label: 'coverage', message: 'job or coverage not found' })
-
-t.create('cobertura: job not found')
- .get(
- '/cobertura.json?jobUrl=https://jenkins.sqlalchemy.org/job/does-not-exist'
- )
- .expectBadge({ label: 'coverage', message: 'job or coverage not found' })
-
-t.create('cobertura: job found')
- .get(
- '/cobertura.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage'
- )
+t.create('code coverage API: job found')
+ .get('.json?jobUrl=https://jenkins.mm12.xyz/jenkins/job/nmfu/job/master')
.expectBadge({ label: 'coverage', message: isIntegerPercentage })
t.create('code coverage API: job not found')
- .get(
- '/api.json?jobUrl=https://jenkins.library.illinois.edu/job/does-not-exist'
- )
+ .get('.json?jobUrl=https://jenkins.mm12.xyz/jenkins/job/does-not-exist')
.expectBadge({ label: 'coverage', message: 'job or coverage not found' })
-
-t.create('code coverage API: job found')
- .get(
- '/api.json?jobUrl=https://jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master'
- )
- .expectBadge({ label: 'coverage', message: isIntegerPercentage })
diff --git a/services/jenkins/jenkins-plugin-installs.service.js b/services/jenkins/jenkins-plugin-installs.service.js
index 714120a0a5e11..69624dfcd77d1 100644
--- a/services/jenkins/jenkins-plugin-installs.service.js
+++ b/services/jenkins/jenkins-plugin-installs.service.js
@@ -1,10 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schemaInstallations = Joi.object()
.keys({
@@ -15,114 +12,56 @@ const schemaInstallations = Joi.object()
})
.required()
-const schemaInstallationsPerVersion = Joi.object()
- .keys({
- installationsPerVersion: Joi.object()
- .required()
- .pattern(Joi.string(), nonNegativeInteger)
- .min(1),
- })
- .required()
+export default class JenkinsPluginInstalls extends BaseJsonService {
+ static category = 'downloads'
-module.exports = class JenkinsPluginInstalls extends BaseJsonService {
- static _getSchema(version) {
- if (version) {
- return schemaInstallationsPerVersion
- } else {
- return schemaInstallations
- }
- }
-
- static _getLabel(version) {
- if (version) {
- return `installs@${version}`
- } else {
- return 'installs'
- }
+ static route = {
+ base: 'jenkins/plugin/i',
+ pattern: ':plugin/:version?',
}
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'jenkins/plugin/i',
- pattern: ':plugin/:version?',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Jenkins Plugin installs',
- pattern: ':plugin',
- namedParams: {
- plugin: 'view-job-filters',
- },
- staticPreview: this.render({
- label: this._getLabel(),
- installs: 10247,
- }),
- },
- {
- title: 'Jenkins Plugin installs (version)',
- pattern: ':plugin/:version',
- namedParams: {
- plugin: 'view-job-filters',
- version: '1.26',
- },
- staticPreview: this.render({
- label: this._getLabel('1.26'),
- installs: 955,
+ static openApi = {
+ '/jenkins/plugin/i/{plugin}': {
+ get: {
+ summary: 'Jenkins Plugin installs',
+ parameters: pathParams({
+ name: 'plugin',
+ example: 'view-job-filters',
}),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'installs' }
- }
+ static defaultBadgeData = { label: 'installs' }
- static render({ label, installs }) {
- return {
- label,
- message: metric(installs),
- color: downloadCountColor(installs),
- }
+ static render({ installs: downloads }) {
+ return renderDownloadsBadge({ downloads })
}
- async fetch({ plugin, version }) {
- const url = `https://stats.jenkins.io/plugin-installation-trend/${plugin}.stats.json`
- const schema = this.constructor._getSchema(version)
+ async fetch({ plugin }) {
return this._requestJson({
- url,
- schema,
- errorMessages: {
+ url: `https://stats.jenkins.io/plugin-installation-trend/${plugin}.stats.json`,
+ schema: schemaInstallations,
+ httpErrors: {
404: 'plugin not found',
},
})
}
- async handle({ plugin, version }) {
- const label = this.constructor._getLabel(version)
- const json = await this.fetch({ plugin, version })
+ static transform({ json }) {
+ const latestDate = Object.keys(json.installations).sort().slice(-1)[0]
+ return { installs: json.installations[latestDate] }
+ }
- let installs
+ async handle({ plugin, version }) {
if (version) {
- installs = json.installationsPerVersion[version]
- if (!installs) {
- throw new NotFound({
- prettyMessage: 'version not found',
- })
+ return {
+ message: 'no longer available per version',
+ color: 'lightgrey',
}
- } else {
- const latestDate = Object.keys(json.installations)
- .sort()
- .slice(-1)[0]
- installs = json.installations[latestDate]
}
-
- return this.constructor.render({ label, installs })
+ const json = await this.fetch({ plugin })
+ const { installs } = this.constructor.transform({ json })
+ return this.constructor.render({ installs })
}
}
diff --git a/services/jenkins/jenkins-plugin-installs.tester.js b/services/jenkins/jenkins-plugin-installs.tester.js
index 2a245dfb2b992..085b7d9bae8c9 100644
--- a/services/jenkins/jenkins-plugin-installs.tester.js
+++ b/services/jenkins/jenkins-plugin-installs.tester.js
@@ -1,16 +1,13 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// total installs
-t.create('total installs | valid')
- .get('/view-job-filters.json')
- .expectBadge({
- label: 'installs',
- message: isMetric,
- })
+t.create('total installs | valid').get('/view-job-filters.json').expectBadge({
+ label: 'installs',
+ message: isMetric,
+})
t.create('total installs | not found')
.get('/not-a-plugin.json')
@@ -18,24 +15,10 @@ t.create('total installs | not found')
// version installs
-t.create('version installs | valid: numeric version')
+t.create('version installs | no longer available')
.get('/view-job-filters/1.26.json')
.expectBadge({
- label: 'installs@1.26',
- message: isMetric,
- })
-
-t.create('version installs | valid: alphanumeric version')
- .get('/build-failure-analyzer/1.17.2-DRE3.14.json')
- .expectBadge({
- label: 'installs@1.17.2-DRE3.14',
- message: isMetric,
+ label: 'installs',
+ message: 'no longer available per version',
+ color: 'lightgrey',
})
-
-t.create('version installs | not found: non-existent plugin')
- .get('/not-a-plugin/1.26.json')
- .expectBadge({ label: 'installs', message: 'plugin not found' })
-
-t.create('version installs | not found: non-existent version')
- .get('/view-job-filters/1.1-NOT-FOUND.json')
- .expectBadge({ label: 'installs', message: 'version not found' })
diff --git a/services/jenkins/jenkins-plugin-version.service.js b/services/jenkins/jenkins-plugin-version.service.js
index ad26ff73fc947..d40187cd0df10 100644
--- a/services/jenkins/jenkins-plugin-version.service.js
+++ b/services/jenkins/jenkins-plugin-version.service.js
@@ -1,50 +1,37 @@
-'use strict'
+import { getCachedResource } from '../../core/base-service/resource-cache.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseService, NotFound, pathParams } from '../index.js'
-const { promisify } = require('util')
-const { regularUpdate } = require('../../core/legacy/regular-update')
-const { renderVersionBadge } = require('../version')
-const { BaseService, NotFound } = require('..')
+export default class JenkinsPluginVersion extends BaseService {
+ static category = 'version'
-module.exports = class JenkinsPluginVersion extends BaseService {
- static get category() {
- return 'version'
+ static route = {
+ base: 'jenkins/plugin/v',
+ pattern: ':plugin',
}
- static get route() {
- return {
- base: 'jenkins/plugin/v',
- pattern: ':plugin',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Jenkins Plugins',
- namedParams: {
- plugin: 'blueocean',
- },
- staticPreview: {
- label: 'plugin',
- message: 'v1.10.1',
- color: 'blue',
- },
+ static openApi = {
+ '/jenkins/plugin/v/{plugin}': {
+ get: {
+ summary: 'Jenkins Plugin Version',
+ parameters: pathParams({
+ name: 'plugin',
+ example: 'blueocean',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'plugin' }
- }
+ static defaultBadgeData = { label: 'plugin' }
static render({ version }) {
return renderVersionBadge({ version })
}
async fetch() {
- return promisify(regularUpdate)({
+ return getCachedResource({
url: 'https://updates.jenkins-ci.org/current/update-center.actual.json',
- intervalMillis: 4 * 3600 * 1000,
+ ttl: 4 * 3600 * 1000, // 4 hours in milliseconds
scraper: json =>
Object.keys(json.plugins).reduce((previous, current) => {
previous[current] = json.plugins[current].version
diff --git a/services/jenkins/jenkins-plugin-version.tester.js b/services/jenkins/jenkins-plugin-version.tester.js
index 2681a9eb0eedf..da1eee8349d79 100644
--- a/services/jenkins/jenkins-plugin-version.tester.js
+++ b/services/jenkins/jenkins-plugin-version.tester.js
@@ -1,21 +1,18 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({
+export const t = new ServiceTester({
id: 'jenkins-plugin',
title: 'JenkinsPluginVersion',
pathPrefix: '/jenkins',
})
-module.exports = t
t.create('latest version')
.get('/plugin/v/blueocean.json')
.intercept(nock =>
nock('https://updates.jenkins-ci.org')
.get('/current/update-center.actual.json')
- .reply(200, { plugins: { blueocean: { version: '1.1.6' } } })
+ .reply(200, { plugins: { blueocean: { version: '1.1.6' } } }),
)
.expectBadge({
label: 'plugin',
@@ -27,7 +24,7 @@ t.create('version 0')
.intercept(nock =>
nock('https://updates.jenkins-ci.org')
.get('/current/update-center.actual.json')
- .reply(200, { plugins: { blueocean: { version: '0' } } })
+ .reply(200, { plugins: { blueocean: { version: '0' } } }),
)
.expectBadge({
label: 'plugin',
@@ -39,6 +36,6 @@ t.create('inexistent artifact')
.intercept(nock =>
nock('https://updates.jenkins-ci.org')
.get('/current/update-center.actual.json')
- .reply(200, { plugins: { blueocean: { version: '1.1.6' } } })
+ .reply(200, { plugins: { blueocean: { version: '1.1.6' } } }),
)
.expectBadge({ label: 'plugin', message: 'plugin not found' })
diff --git a/services/jenkins/jenkins-tests-redirector.service.js b/services/jenkins/jenkins-tests-redirector.service.js
index 138ded80bc354..ebda3f48f1007 100644
--- a/services/jenkins/jenkins-tests-redirector.service.js
+++ b/services/jenkins/jenkins-tests-redirector.service.js
@@ -1,31 +1,25 @@
-'use strict'
-
-const { redirector } = require('..')
-const { buildRedirectUrl } = require('./jenkins-common')
+import { retiredService } from '../index.js'
const commonProps = {
category: 'build',
- transformPath: () => '/jenkins/tests',
- transformQueryParams: ({ protocol, host, job }) => ({
- jobUrl: buildRedirectUrl({ protocol, host, job }),
- }),
+ label: 'jenkins',
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}
-module.exports = [
- redirector({
+export default [
+ retiredService({
route: {
base: 'jenkins/t',
pattern: ':protocol(http|https)/:host/:job+',
},
- dateAdded: new Date('2019-04-20'),
...commonProps,
}),
- redirector({
+ retiredService({
route: {
base: 'jenkins/tests',
pattern: ':protocol(http|https)/:host/:job+',
},
- dateAdded: new Date('2019-11-29'),
...commonProps,
}),
]
diff --git a/services/jenkins/jenkins-tests-redirector.tester.js b/services/jenkins/jenkins-tests-redirector.tester.js
index 3f6cfc9de672b..7053644d9622d 100644
--- a/services/jenkins/jenkins-tests-redirector.tester.js
+++ b/services/jenkins/jenkins-tests-redirector.tester.js
@@ -1,29 +1,25 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JenkinsTestsRedirector',
title: 'JenkinsTestsRedirector',
pathPrefix: '/jenkins',
-}))
+})
t.create('old tests prefix + job url in path')
.get(
- '/t/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.svg'
- )
- .expectRedirect(
- `/jenkins/tests.svg?jobUrl=${encodeURIComponent(
- 'https://jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily'
- )}`
+ '/t/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.json',
)
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('new tests prefix + job url in path')
.get(
- '/tests/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.svg'
- )
- .expectRedirect(
- `/jenkins/tests.svg?jobUrl=${encodeURIComponent(
- 'https://jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily'
- )}`
+ '/tests/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.json',
)
+ .expectBadge({
+ label: 'jenkins',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/jenkins/jenkins-tests.service.js b/services/jenkins/jenkins-tests.service.js
index 243990eb3aa3c..358187e54b44e 100644
--- a/services/jenkins/jenkins-tests.service.js
+++ b/services/jenkins/jenkins-tests.service.js
@@ -1,19 +1,18 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- documentation,
+import Joi from 'joi'
+import {
+ documentation as description,
testResultQueryParamSchema,
+ testResultOpenApiQueryParams,
renderTestResultBadge,
-} = require('../test-results')
-const { optionalNonNegativeInteger } = require('../validators')
-const { InvalidResponse } = require('..')
-const JenkinsBase = require('./jenkins-base')
-const {
+} from '../test-results.js'
+import { optionalNonNegativeInteger } from '../validators.js'
+import { InvalidResponse, queryParam } from '../index.js'
+import JenkinsBase from './jenkins-base.js'
+import {
buildTreeParamQueryString,
buildUrl,
queryParamSchema,
-} = require('./jenkins-common')
+} from './jenkins-common.js'
// In the API response, the `actions` array can be empty, and when it is not empty it will contain a
// mix of objects. Some will be empty objects, and several will not have the test count properties.
@@ -30,53 +29,37 @@ const schema = Joi.object({
totalCount: optionalNonNegativeInteger,
failCount: optionalNonNegativeInteger,
skipCount: optionalNonNegativeInteger,
- })
+ }),
)
.required(),
}).required()
-module.exports = class JenkinsTests extends JenkinsBase {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'jenkins',
- pattern: 'tests',
- queryParamSchema: queryParamSchema.concat(testResultQueryParamSchema),
- }
+export default class JenkinsTests extends JenkinsBase {
+ static category = 'test-results'
+ static route = {
+ base: 'jenkins',
+ pattern: 'tests',
+ queryParamSchema: queryParamSchema.concat(testResultQueryParamSchema),
}
- static get examples() {
- return [
- {
- title: 'Jenkins tests',
- namedParams: {},
- queryParams: {
- compact_message: null,
- passed_label: 'passed',
- failed_label: 'failed',
- skipped_label: 'skipped',
- jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
- },
- staticPreview: this.render({
- passed: 477,
- failed: 2,
- skipped: 0,
- total: 479,
- isCompact: false,
- }),
- documentation,
+ static openApi = {
+ '/jenkins/tests': {
+ get: {
+ summary: 'Jenkins Tests',
+ description,
+ parameters: [
+ queryParam({
+ name: 'jobUrl',
+ example: 'https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
+ required: true,
+ }),
+ ...testResultOpenApiQueryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'tests',
- }
- }
+ static defaultBadgeData = { label: 'tests' }
static render({
passed,
@@ -119,19 +102,19 @@ module.exports = class JenkinsTests extends JenkinsBase {
async handle(
namedParams,
{
- disableStrictSSL,
jobUrl,
compact_message: compactMessage,
passed_label: passedLabel,
failed_label: failedLabel,
skipped_label: skippedLabel,
- }
+ },
) {
const json = await this.fetch({
url: buildUrl({ jobUrl }),
schema,
- qs: buildTreeParamQueryString('actions[failCount,skipCount,totalCount]'),
- disableStrictSSL,
+ searchParams: buildTreeParamQueryString(
+ 'actions[failCount,skipCount,totalCount]',
+ ),
})
const { passed, failed, skipped, total } = this.transform({ json })
return this.constructor.render({
diff --git a/services/jenkins/jenkins-tests.spec.js b/services/jenkins/jenkins-tests.spec.js
new file mode 100644
index 0000000000000..5f424c3ec2bf9
--- /dev/null
+++ b/services/jenkins/jenkins-tests.spec.js
@@ -0,0 +1,27 @@
+import { testAuth } from '../test-helpers.js'
+import JenkinsTests from './jenkins-tests.service.js'
+
+const authConfigOverride = {
+ public: {
+ services: {
+ jenkins: {
+ authorizedOrigins: ['https://ci.freebsd.org'],
+ },
+ },
+ },
+}
+
+describe('JenkinsTests', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ JenkinsTests,
+ 'BasicAuth',
+ { actions: [{ totalCount: 3, failCount: 2, skipCount: 1 }] },
+ {
+ configOverride: authConfigOverride,
+ },
+ )
+ })
+ })
+})
diff --git a/services/jenkins/jenkins-tests.tester.js b/services/jenkins/jenkins-tests.tester.js
index 7b4f490ffbd4a..6e545f3e980f9 100644
--- a/services/jenkins/jenkins-tests.tester.js
+++ b/services/jenkins/jenkins-tests.tester.js
@@ -1,12 +1,11 @@
-'use strict'
-
-const {
+import {
isDefaultTestTotals,
isDefaultCompactTestTotals,
isCustomTestTotals,
isCustomCompactTestTotals,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// The below page includes links to various publicly accessible Jenkins instances
// although many of the links are dead, it is is still a helpful resource for finding
@@ -14,34 +13,34 @@ const t = (module.exports = require('../tester').createServiceTester())
// https://wiki.jenkins.io/pages/viewpage.action?pageId=58001258
t.create('Test status')
- .get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage')
+ .get('/tests.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-test')
.expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status with compact message')
.get(
- '/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
+ '/tests.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
{
qs: { compact_message: null },
- }
+ },
)
.expectBadge({ label: 'tests', message: isDefaultCompactTestTotals })
t.create('Test status with custom labels')
.get(
- '/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
+ '/tests.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
{
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
- }
+ },
)
.expectBadge({ label: 'tests', message: isCustomTestTotals })
t.create('Test status with compact message and custom labels')
.get(
- '/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
+ '/tests.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-test',
{
qs: {
compact_message: null,
@@ -49,7 +48,7 @@ t.create('Test status with compact message and custom labels')
failed_label: '🤦♀️',
skipped_label: '🤷',
},
- }
+ },
)
.expectBadge({
label: 'tests',
@@ -57,11 +56,9 @@ t.create('Test status with compact message and custom labels')
})
t.create('Test status on job with no tests')
- .get(
- '/tests.json?jobUrl=https://ci.eclipse.org/openj9/job/Build-Doc-Push_to_ghpages'
- )
+ .get('/tests.json?jobUrl=https://ci.freebsd.org/job/FreeBSD-main-amd64-build')
.expectBadge({ label: 'tests', message: 'no tests found' })
t.create('Test status on non-existent job')
- .get('/tests.json?jobUrl=https://ci.eclipse.org/openj9/job/does-not-exist')
+ .get('/tests.json?jobUrl=https://ci.freebsd.org/job/does-not-exist')
.expectBadge({ label: 'tests', message: 'instance or job not found' })
diff --git a/services/jetbrains/jetbrains-base.js b/services/jetbrains/jetbrains-base.js
index 877fb413fda06..fe3c497c4797d 100644
--- a/services/jetbrains/jetbrains-base.js
+++ b/services/jetbrains/jetbrains-base.js
@@ -1,11 +1,30 @@
-'use strict'
+import { BaseXmlService, NotFound } from '../index.js'
+import { parseJson } from '../../core/base-service/json.js'
-const { BaseXmlService, NotFound } = require('..')
+/*
+JetBrains is a bit awkward. Sometimes we want to call an XML API
+and sometimes we want to call a JSON API so we need a mongrel base class.
+When the legacy IntelliJ (XML) API is retired we can simplify all this and
+switch JetbrainsDownloads, JetbrainsRating and JetbrainsVersion to just
+inherit from BaseJsonService directly.
+*/
+export default class JetbrainsBase extends BaseXmlService {
+ static _isLegacyPluginId(pluginId) {
+ return !pluginId.match(/^([0-9])+/)
+ }
+
+ static _cleanPluginId(pluginId) {
+ const match = pluginId.match(/^([0-9])+/)
+ if (match) {
+ return match[0]
+ }
+ return pluginId
+ }
-module.exports = class JetbrainsBase extends BaseXmlService {
+ // xml
static _validate(data, schema) {
if (data['plugin-repository'] === '') {
- // Note the 'not found' response from JetBrains Plugins Repository is:
+ // Note the 'not found' response from JetBrains IntelliJ API is:
// status code = 200,
// body =
// which is parsed to object = { 'plugin-repository': '' }
@@ -14,9 +33,9 @@ module.exports = class JetbrainsBase extends BaseXmlService {
return super._validate(data, schema)
}
- async fetchPackageData({ pluginId, schema }) {
+ async fetchIntelliJPluginData({ pluginId, schema }) {
const parserOptions = {
- parseNodeValue: false,
+ parseTagValue: false,
ignoreAttributes: false,
}
return this._requestXml({
@@ -25,4 +44,27 @@ module.exports = class JetbrainsBase extends BaseXmlService {
parserOptions,
})
}
+
+ // json
+ _parseJson(buffer) {
+ return parseJson(buffer)
+ }
+
+ static _validateJson(data, schema) {
+ return super._validate(data, schema)
+ }
+
+ async _requestJson({ schema, url, options = {}, httpErrors = {} }) {
+ const mergedOptions = {
+ ...{ headers: { Accept: 'application/json' } },
+ ...options,
+ }
+ const { buffer } = await this._request({
+ url,
+ options: mergedOptions,
+ httpErrors,
+ })
+ const json = this._parseJson(buffer)
+ return this.constructor._validateJson(json, schema)
+ }
}
diff --git a/services/jetbrains/jetbrains-downloads.service.js b/services/jetbrains/jetbrains-downloads.service.js
index 0baff0afe6029..74f16d25c692e 100644
--- a/services/jetbrains/jetbrains-downloads.service.js
+++ b/services/jetbrains/jetbrains-downloads.service.js
@@ -1,12 +1,10 @@
-'use strict'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import JetbrainsBase from './jetbrains-base.js'
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const JetbrainsBase = require('./jetbrains-base')
-
-const schema = Joi.object({
+const intelliJschema = Joi.object({
'plugin-repository': Joi.object({
category: Joi.object({
'idea-plugin': Joi.array()
@@ -14,7 +12,7 @@ const schema = Joi.object({
.items(
Joi.object({
'@_downloads': nonNegativeInteger,
- })
+ }),
)
.single()
.required(),
@@ -22,41 +20,50 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class JetbrainsDownloads extends JetbrainsBase {
- static get category() {
- return 'downloads'
- }
+const jetbrainsSchema = Joi.object({ downloads: nonNegativeInteger }).required()
- static get route() {
- return {
- base: 'jetbrains/plugin/d',
- pattern: ':pluginId',
- }
+export default class JetbrainsDownloads extends JetbrainsBase {
+ static category = 'downloads'
+
+ static route = {
+ base: 'jetbrains/plugin/d',
+ pattern: ':pluginId',
}
- static get examples() {
- return [
- {
- title: 'JetBrains IntelliJ plugins',
- namedParams: {
- pluginId: '1347-scala',
- },
- staticPreview: this.render({ downloads: 10200000 }),
+ static openApi = {
+ '/jetbrains/plugin/d/{pluginId}': {
+ get: {
+ summary: 'JetBrains Plugin Downloads',
+ parameters: pathParams({
+ name: 'pluginId',
+ example: '1347',
+ }),
},
- ]
+ },
}
- static render({ downloads }) {
- return {
- message: `${metric(downloads)}`,
- color: downloadCountColor(downloads),
+ async handle({ pluginId }) {
+ let downloads
+ if (this.constructor._isLegacyPluginId(pluginId)) {
+ const intelliJPluginData = await this.fetchIntelliJPluginData({
+ pluginId,
+ schema: intelliJschema,
+ })
+ downloads =
+ intelliJPluginData['plugin-repository'].category['idea-plugin'][0][
+ '@_downloads'
+ ]
+ } else {
+ const jetbrainsPluginData = await this._requestJson({
+ schema: jetbrainsSchema,
+ url: `https://plugins.jetbrains.com/api/plugins/${this.constructor._cleanPluginId(
+ pluginId,
+ )}`,
+ httpErrors: { 400: 'not found' },
+ })
+ downloads = jetbrainsPluginData.downloads
}
- }
- async handle({ pluginId }) {
- const pluginData = await this.fetchPackageData({ pluginId, schema })
- const downloads =
- pluginData['plugin-repository'].category['idea-plugin'][0]['@_downloads']
- return this.constructor.render({ downloads })
+ return renderDownloadsBadge({ downloads })
}
}
diff --git a/services/jetbrains/jetbrains-downloads.tester.js b/services/jetbrains/jetbrains-downloads.tester.js
index 1a95d5dae518f..03090600936dd 100644
--- a/services/jetbrains/jetbrains-downloads.tester.js
+++ b/services/jetbrains/jetbrains-downloads.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('downloads (number as a plugin id)')
.get('/7495.json')
@@ -15,12 +14,21 @@ t.create('downloads (user friendly plugin id)')
.get('/1347-scala.json')
.expectBadge({ label: 'downloads', message: isMetric })
-t.create('downloads')
+t.create('downloads (numeric id)')
.get('/9435.json')
+ .intercept(nock =>
+ nock('https://plugins.jetbrains.com')
+ .get('/api/plugins/9435')
+ .reply(200, { downloads: 2 }),
+ )
+ .expectBadge({ label: 'downloads', message: '2' })
+
+t.create('downloads (string id)')
+ .get('/io.harply.plugin.json')
.intercept(
nock =>
nock('https://plugins.jetbrains.com')
- .get('/plugins/list?pluginId=9435')
+ .get('/plugins/list?pluginId=io.harply.plugin')
.reply(
200,
`
@@ -28,14 +36,22 @@ t.create('downloads')
- `
+ `,
),
{
'Content-Type': 'text/xml;charset=UTF-8',
- }
+ },
)
.expectBadge({ label: 'downloads', message: '2' })
-t.create('unknown plugin')
+t.create('unknown plugin (string id)')
.get('/unknown-plugin.json')
.expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('unknown plugin (numeric id)')
+ .get('/9999999999999.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('unknown plugin (mixed id)')
+ .get('/9999999999999-abc.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
diff --git a/services/jetbrains/jetbrains-rating.service.js b/services/jetbrains/jetbrains-rating.service.js
index c57512cba06ff..5fe197e66671d 100644
--- a/services/jetbrains/jetbrains-rating.service.js
+++ b/services/jetbrains/jetbrains-rating.service.js
@@ -1,13 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { starRating } = require('../text-formatters')
-const { colorScale } = require('../color-formatters')
-const JetbrainsBase = require('./jetbrains-base')
+import Joi from 'joi'
+import { starRating } from '../text-formatters.js'
+import { colorScale } from '../color-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import JetbrainsBase from './jetbrains-base.js'
const pluginRatingColor = colorScale([2, 3, 4])
-const schema = Joi.object({
+const intelliJschema = Joi.object({
'plugin-repository': Joi.object({
category: Joi.object({
'idea-plugin': Joi.array()
@@ -15,7 +14,7 @@ const schema = Joi.object({
.items(
Joi.object({
rating: Joi.string().required(),
- })
+ }),
)
.single()
.required(),
@@ -23,48 +22,42 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class JetbrainsRating extends JetbrainsBase {
- static get category() {
- return 'rating'
- }
+const jetbrainsSchema = Joi.object({
+ votes: Joi.object()
+ .pattern(Joi.string().required(), Joi.number().required())
+ .required(),
+ meanVotes: Joi.number().min(0).required(),
+ meanRating: Joi.number().min(0).required(),
+}).required()
- static get route() {
- return {
- base: 'jetbrains/plugin/r',
- pattern: ':format(rating|stars)/:pluginId',
- }
+export default class JetbrainsRating extends JetbrainsBase {
+ static category = 'rating'
+
+ static route = {
+ base: 'jetbrains/plugin/r',
+ pattern: ':format(rating|stars)/:pluginId',
}
- static get examples() {
- return [
- {
- title: 'JetBrains IntelliJ Plugins',
- pattern: 'rating/:pluginId',
- namedParams: {
- pluginId: '11941-automatic-power-saver',
- },
- staticPreview: this.render({
- rating: '4.5',
- format: 'rating',
- }),
- },
- {
- title: 'JetBrains IntelliJ Plugins',
- pattern: 'stars/:pluginId',
- namedParams: {
- pluginId: '11941-automatic-power-saver',
- },
- staticPreview: this.render({
- rating: '4.5',
- format: 'stars',
- }),
+ static openApi = {
+ '/jetbrains/plugin/r/{format}/{pluginId}': {
+ get: {
+ summary: 'JetBrains Plugin Rating',
+ parameters: pathParams(
+ {
+ name: 'format',
+ example: 'rating',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ },
+ {
+ name: 'pluginId',
+ example: '11941',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'rating' }
- }
+ static defaultBadgeData = { label: 'rating' }
static render({ rating, format }) {
const message =
@@ -78,9 +71,42 @@ module.exports = class JetbrainsRating extends JetbrainsBase {
}
async handle({ format, pluginId }) {
- const pluginData = await this.fetchPackageData({ pluginId, schema })
- const pluginRating =
- pluginData['plugin-repository'].category['idea-plugin'][0].rating
- return this.constructor.render({ rating: pluginRating, format })
+ let rating
+ if (this.constructor._isLegacyPluginId(pluginId)) {
+ const intelliJPluginData = await this.fetchIntelliJPluginData({
+ pluginId,
+ schema: intelliJschema,
+ })
+ rating =
+ intelliJPluginData['plugin-repository'].category['idea-plugin'][0]
+ .rating
+ } else {
+ const jetbrainsPluginData = await this._requestJson({
+ schema: jetbrainsSchema,
+ url: `https://plugins.jetbrains.com/api/plugins/${this.constructor._cleanPluginId(
+ pluginId,
+ )}/rating`,
+ httpErrors: { 400: 'not found' },
+ })
+
+ let voteSum = 0
+ let voteCount = 0
+ const votes = jetbrainsPluginData.votes
+ Object.entries(votes).forEach(([rating, votes]) => {
+ voteSum += parseInt(rating) * votes
+ voteCount += votes
+ })
+ const meanRating = jetbrainsPluginData.meanRating
+
+ if (voteCount === 0) {
+ throw new NotFound({ prettyMessage: 'No Plugin Ratings' })
+ }
+
+ // JetBrains Plugin Rating Formula from:
+ // https://plugins.jetbrains.com/docs/marketplace/plugins-rating.html
+ rating = (voteSum + 2 * meanRating) / (voteCount + 2)
+ }
+
+ return this.constructor.render({ rating, format })
}
}
diff --git a/services/jetbrains/jetbrains-rating.tester.js b/services/jetbrains/jetbrains-rating.tester.js
index 1388905f5eb08..14cdc2b222c4e 100644
--- a/services/jetbrains/jetbrains-rating.tester.js
+++ b/services/jetbrains/jetbrains-rating.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex, isStarRating } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex, isStarRating } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isRating = withRegex(/^(([0-4](.?([0-9]))?)|5)\/5$/)
@@ -17,10 +16,18 @@ t.create('rating number (number as a plugin id)')
.get('/rating/11941.json')
.expectBadge({ label: 'rating', message: isRating })
-t.create('rating number for unknown plugin')
+t.create('rating number for unknown plugin (string)')
.get('/rating/unknown-plugin.json')
.expectBadge({ label: 'rating', message: 'not found' })
+t.create('rating stars for unknown plugin (numeric)')
+ .get('/stars/9999999999999.json')
+ .expectBadge({ label: 'rating', message: 'not found' })
+
+t.create('rating stars for unknown plugin (mixed)')
+ .get('/stars/9999999999999-abc.json')
+ .expectBadge({ label: 'rating', message: 'not found' })
+
t.create('rating stars (user friendly plugin id)')
.get('/stars/11941-automatic-power-saver.json')
.expectBadge({ label: 'rating', message: isStarRating })
@@ -33,52 +40,109 @@ t.create('rating stars (number as a plugin id)')
.get('/stars/11941.json')
.expectBadge({ label: 'rating', message: isStarRating })
-t.create('rating stars for unknown plugin')
+t.create('rating stars for unknown plugin (string id)')
.get('/stars/unknown-plugin.json')
.expectBadge({ label: 'rating', message: 'not found' })
-t.create('rating number')
+t.create('rating stars for unknown plugin (numeric id)')
+ .get('/stars/9999999999999.json')
+ .expectBadge({ label: 'rating', message: 'not found' })
+
+t.create('rating stars for unknown plugin (mixed id)')
+ .get('/stars/9999999999999-abc.json')
+ .expectBadge({ label: 'rating', message: 'not found' })
+
+t.create('rating number (numeric id)')
.get('/rating/11941.json')
+ .intercept(nock =>
+ nock('https://plugins.jetbrains.com')
+ .get('/api/plugins/11941/rating')
+ .reply(200, {
+ votes: {
+ 4: 1,
+ 5: 4,
+ },
+ meanVotes: 2,
+ meanRating: 4.15669,
+ }),
+ )
+ .expectBadge({ label: 'rating', message: '4.6/5' })
+
+t.create('rating number for "no vote" plugin (numeric id)')
+ .get('/rating/10998.json')
+ .intercept(nock =>
+ nock('https://plugins.jetbrains.com')
+ .get('/api/plugins/10998/rating')
+ .reply(200, {
+ votes: {},
+ meanVotes: 2,
+ meanRating: 4.15669,
+ }),
+ )
+ .expectBadge({ label: 'rating', message: 'No Plugin Ratings' })
+
+t.create('rating number (string id)')
+ .get('/rating/com.chriscarini.jetbrains.jetbrains-auto-power-saver.json')
.intercept(
nock =>
nock('https://plugins.jetbrains.com')
- .get('/plugins/list?pluginId=11941')
+ .get(
+ '/plugins/list?pluginId=com.chriscarini.jetbrains.jetbrains-auto-power-saver',
+ )
.reply(
200,
`
- 4.5
+ 4.4848
- `
+ `,
),
{
'Content-Type': 'text/xml;charset=UTF-8',
- }
+ },
)
.expectBadge({ label: 'rating', message: '4.5/5' })
-t.create('rating stars')
+t.create('rating stars (numeric id)')
.get('/stars/11941.json')
+ .intercept(nock =>
+ nock('https://plugins.jetbrains.com')
+ .get('/api/plugins/11941/rating')
+ .reply(200, {
+ votes: {
+ 4: 1,
+ 5: 4,
+ },
+ meanVotes: 2,
+ meanRating: 4.15669,
+ }),
+ )
+ .expectBadge({ label: 'rating', message: '★★★★½' })
+
+t.create('rating stars (string id)')
+ .get('/stars/com.chriscarini.jetbrains.jetbrains-auto-power-saver.json')
.intercept(
nock =>
nock('https://plugins.jetbrains.com')
- .get('/plugins/list?pluginId=11941')
+ .get(
+ '/plugins/list?pluginId=com.chriscarini.jetbrains.jetbrains-auto-power-saver',
+ )
.reply(
200,
`
- 4.5
+ 4.4848
- `
+ `,
),
{
'Content-Type': 'text/xml;charset=UTF-8',
- }
+ },
)
.expectBadge({ label: 'rating', message: '★★★★½' })
diff --git a/services/jetbrains/jetbrains-version.service.js b/services/jetbrains/jetbrains-version.service.js
index 6c845a936c413..8d349167bc5c3 100644
--- a/services/jetbrains/jetbrains-version.service.js
+++ b/services/jetbrains/jetbrains-version.service.js
@@ -1,10 +1,9 @@
-'use strict'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import JetbrainsBase from './jetbrains-base.js'
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const JetbrainsBase = require('./jetbrains-base')
-
-const schema = Joi.object({
+const intelliJschema = Joi.object({
'plugin-repository': Joi.object({
category: Joi.object({
'idea-plugin': Joi.array()
@@ -12,7 +11,7 @@ const schema = Joi.object({
.items(
Joi.object({
version: Joi.string().required(),
- })
+ }),
)
.single()
.required(),
@@ -20,42 +19,62 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class JetbrainsVersion extends JetbrainsBase {
- static get category() {
- return 'version'
- }
+const jetbrainsSchema = Joi.array()
+ .min(1)
+ .items(
+ Joi.object({
+ version: Joi.string().required(),
+ }).required(),
+ )
+ .required()
- static get route() {
- return {
- base: 'jetbrains/plugin/v',
- pattern: ':pluginId',
- }
+export default class JetbrainsVersion extends JetbrainsBase {
+ static category = 'version'
+
+ static route = {
+ base: 'jetbrains/plugin/v',
+ pattern: ':pluginId',
}
- static get examples() {
- return [
- {
- title: 'JetBrains IntelliJ Plugins',
- namedParams: {
- pluginId: '9630-a8translate',
- },
- staticPreview: this.render({ version: 'v1.7' }),
+ static openApi = {
+ '/jetbrains/plugin/v/{pluginId}': {
+ get: {
+ summary: 'JetBrains Plugin Version',
+ parameters: pathParams({
+ name: 'pluginId',
+ example: '9630',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'jetbrains plugin' }
- }
+ static defaultBadgeData = { label: 'jetbrains plugin' }
static render({ version }) {
return renderVersionBadge({ version })
}
async handle({ pluginId }) {
- const pluginData = await this.fetchPackageData({ pluginId, schema })
- const version =
- pluginData['plugin-repository'].category['idea-plugin'][0].version
+ let version
+ if (this.constructor._isLegacyPluginId(pluginId)) {
+ const intelliJPluginData = await this.fetchIntelliJPluginData({
+ pluginId,
+ schema: intelliJschema,
+ })
+ version =
+ intelliJPluginData['plugin-repository'].category['idea-plugin'][0]
+ .version
+ } else {
+ const jetbrainsPluginData = await this._requestJson({
+ schema: jetbrainsSchema,
+ url: `https://plugins.jetbrains.com/api/plugins/${this.constructor._cleanPluginId(
+ pluginId,
+ )}/updates`,
+ httpErrors: { 400: 'not found' },
+ })
+ version = jetbrainsPluginData[0].version
+ }
+
return this.constructor.render({ version })
}
}
diff --git a/services/jetbrains/jetbrains-version.tester.js b/services/jetbrains/jetbrains-version.tester.js
index eaafce0e65e2d..8c2dbf962be4f 100644
--- a/services/jetbrains/jetbrains-version.tester.js
+++ b/services/jetbrains/jetbrains-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isVPlusDottedVersionNClauses } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('version (user friendly plugin id)')
.get('/1347-scala.json')
@@ -17,19 +16,26 @@ t.create('version (plugin id from plugin.xml)')
message: isVPlusDottedVersionNClauses,
})
-t.create('version (number as a plugin id)')
- .get('/7495.json')
- .expectBadge({
- label: 'jetbrains plugin',
- message: isVPlusDottedVersionNClauses,
- })
+t.create('version (number as a plugin id)').get('/7495.json').expectBadge({
+ label: 'jetbrains plugin',
+ message: isVPlusDottedVersionNClauses,
+})
-t.create('version')
+t.create('version (numeric id)')
.get('/9435.json')
+ .intercept(nock =>
+ nock('https://plugins.jetbrains.com')
+ .get('/api/plugins/9435/updates')
+ .reply(200, [{ version: '1.0' }]),
+ )
+ .expectBadge({ label: 'jetbrains plugin', message: 'v1.0' })
+
+t.create('version (strong id)')
+ .get('/io.harply.plugin.json')
.intercept(
nock =>
nock('https://plugins.jetbrains.com')
- .get('/plugins/list?pluginId=9435')
+ .get('/plugins/list?pluginId=io.harply.plugin')
.reply(
200,
`
@@ -39,14 +45,22 @@ t.create('version')
1.0
- `
+ `,
),
{
'Content-Type': 'text/xml;charset=UTF-8',
- }
+ },
)
.expectBadge({ label: 'jetbrains plugin', message: 'v1.0' })
-t.create('version for unknown plugin')
+t.create('version for unknown plugin (string id)')
.get('/unknown-plugin.json')
.expectBadge({ label: 'jetbrains plugin', message: 'not found' })
+
+t.create('version for unknown plugin (numeric id)')
+ .get('/9999999999999.json')
+ .expectBadge({ label: 'jetbrains plugin', message: 'not found' })
+
+t.create('unknown plugin (mixed id)')
+ .get('/9999999999999-abc.json')
+ .expectBadge({ label: 'jetbrains plugin', message: 'not found' })
diff --git a/services/jira/jira-common.js b/services/jira/jira-common.js
index e8fa67915915c..546435a14f4b2 100644
--- a/services/jira/jira-common.js
+++ b/services/jira/jira-common.js
@@ -1,9 +1,7 @@
-'use strict'
-
const authConfig = {
userKey: 'jira_user',
passKey: 'jira_pass',
serviceKey: 'jira',
}
-module.exports = { authConfig }
+export { authConfig }
diff --git a/services/jira/jira-issue-redirect.service.js b/services/jira/jira-issue-redirect.service.js
index 1c98e875c303f..89a75596a7172 100644
--- a/services/jira/jira-issue-redirect.service.js
+++ b/services/jira/jira-issue-redirect.service.js
@@ -1,18 +1,14 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
- redirector({
+export default [
+ retiredService({
category: 'issue-tracking',
+ label: 'jira',
route: {
base: 'jira/issue',
pattern: ':protocol(http|https)/:hostAndPath(.+)/:issueKey',
},
- transformPath: ({ issueKey }) => `/jira/issue/${issueKey}`,
- transformQueryParams: ({ protocol, hostAndPath }) => ({
- baseUrl: `${protocol}://${hostAndPath}`,
- }),
- dateAdded: new Date('2019-09-14'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
]
diff --git a/services/jira/jira-issue-redirect.tester.js b/services/jira/jira-issue-redirect.tester.js
index 412fcccd50ba9..ddee015b67d1e 100644
--- a/services/jira/jira-issue-redirect.tester.js
+++ b/services/jira/jira-issue-redirect.tester.js
@@ -1,17 +1,14 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JiraIssueRedirect',
title: 'JiraIssueRedirect',
pathPrefix: '/jira/issue',
-}))
+})
t.create('jira issue')
- .get('/https/issues.apache.org/jira/kafka-2896.svg')
- .expectRedirect(
- `/jira/issue/kafka-2896.svg?baseUrl=${encodeURIComponent(
- 'https://issues.apache.org/jira'
- )}`
- )
+ .get('/https/issues.apache.org/jira/kafka-2896.json')
+ .expectBadge({
+ label: 'jira',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/jira/jira-issue.service.js b/services/jira/jira-issue.service.js
index f71bc69ce41f4..2cf6f0eb0ee4b 100644
--- a/services/jira/jira-issue.service.js
+++ b/services/jira/jira-issue.service.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
-const { authConfig } = require('./jira-common')
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { authConfig } from './jira-common.js'
const queryParamSchema = Joi.object({
- baseUrl: optionalUrl.required(),
+ baseUrl: url,
}).required()
const schema = Joi.object({
@@ -20,45 +18,37 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class JiraIssue extends BaseJsonService {
- static get category() {
- return 'issue-tracking'
- }
+export default class JiraIssue extends BaseJsonService {
+ static category = 'issue-tracking'
- static get route() {
- return {
- base: 'jira/issue',
- pattern: ':issueKey',
- queryParamSchema,
- }
+ static route = {
+ base: 'jira/issue',
+ pattern: ':issueKey',
+ queryParamSchema,
}
- static get auth() {
- return authConfig
- }
+ static auth = authConfig
- static get examples() {
- return [
- {
- title: 'JIRA issue',
- namedParams: {
- issueKey: 'KAFKA-2896',
- },
- queryParams: {
- baseUrl: 'https://issues.apache.org/jira',
- },
- staticPreview: this.render({
- issueKey: 'KAFKA-2896',
- statusName: 'Resolved',
- statusColor: 'green',
- }),
+ static openApi = {
+ '/jira/issue/{issueKey}': {
+ get: {
+ summary: 'JIRA issue',
+ parameters: [
+ pathParam({
+ name: 'issueKey',
+ example: 'KAFKA-2896',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://issues.apache.org/jira',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { color: 'lightgrey', label: 'jira' }
- }
+ static defaultBadgeData = { color: 'lightgrey', label: 'jira' }
static render({ issueKey, statusName, statusColor }) {
let color = 'lightgrey'
@@ -87,8 +77,8 @@ module.exports = class JiraIssue extends BaseJsonService {
this.authHelper.withBasicAuth({
schema,
url: `${baseUrl}/rest/api/2/issue/${encodeURIComponent(issueKey)}`,
- errorMessages: { 404: 'issue not found' },
- })
+ httpErrors: { 404: 'issue not found' },
+ }),
)
const issueStatus = json.fields.status
diff --git a/services/jira/jira-issue.spec.js b/services/jira/jira-issue.spec.js
index df5c5e7c86b48..d70aa6aa1995d 100644
--- a/services/jira/jira-issue.spec.js
+++ b/services/jira/jira-issue.spec.js
@@ -1,37 +1,22 @@
-'use strict'
+import { testAuth } from '../test-helpers.js'
+import JiraIssue from './jira-issue.service.js'
+import { config } from './jira-test-helpers.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const JiraIssue = require('./jira-issue.service')
-const { user, pass, host, config } = require('./jira-test-helpers')
-
-describe('JiraIssue', function() {
- cleanUpNockAfterEach()
-
- it('sends the auth information as configured', async function() {
- const scope = nock(`https://${host}`)
- .get(`/rest/api/2/issue/${encodeURIComponent('secure-234')}`)
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, { fields: { status: { name: 'in progress' } } })
-
- expect(
- await JiraIssue.invoke(
- defaultContext,
- config,
+describe('JiraIssue', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ JiraIssue,
+ 'BasicAuth',
{
- issueKey: 'secure-234',
+ fields: {
+ status: {
+ name: 'in progress',
+ },
+ },
},
- { baseUrl: `https://${host}` }
+ { configOverride: config },
)
- ).to.deep.equal({
- label: 'secure-234',
- message: 'in progress',
- color: 'lightgrey',
})
-
- scope.done()
})
})
diff --git a/services/jira/jira-issue.tester.js b/services/jira/jira-issue.tester.js
index 649510f939d34..f7f883fb575b8 100644
--- a/services/jira/jira-issue.tester.js
+++ b/services/jira/jira-issue.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('unknown issue')
.get('/notArealIssue-000.json?baseUrl=https://issues.apache.org/jira')
@@ -11,9 +10,9 @@ t.create('known issue')
.expectBadge({ label: 'kafka-2896', message: 'Resolved' })
t.create('no status color')
- .get('/foo-123.json?baseUrl=http://issues.apache.org/jira')
+ .get('/foo-123.json?baseUrl=https://issues.apache.org/jira')
.intercept(nock =>
- nock('http://issues.apache.org/jira/rest/api/2/issue')
+ nock('https://issues.apache.org/jira/rest/api/2/issue')
.get(`/${encodeURIComponent('foo-123')}`)
.reply(200, {
fields: {
@@ -21,7 +20,7 @@ t.create('no status color')
name: 'pending',
},
},
- })
+ }),
)
.expectBadge({
label: 'foo-123',
@@ -43,7 +42,7 @@ t.create('green status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'bar-345',
@@ -65,7 +64,7 @@ t.create('medium-gray status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'abc-123',
@@ -87,7 +86,7 @@ t.create('yellow status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'test-001',
@@ -109,7 +108,7 @@ t.create('brown status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'zzz-789',
@@ -131,7 +130,7 @@ t.create('warm-red status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'fire-321',
@@ -153,7 +152,7 @@ t.create('blue-gray status color')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'sky-775',
diff --git a/services/jira/jira-sprint-redirect.service.js b/services/jira/jira-sprint-redirect.service.js
index b3f460b044996..840b9f4896ae5 100644
--- a/services/jira/jira-sprint-redirect.service.js
+++ b/services/jira/jira-sprint-redirect.service.js
@@ -1,18 +1,14 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
- redirector({
+export default [
+ retiredService({
category: 'issue-tracking',
+ label: 'jira',
route: {
base: 'jira/sprint',
pattern: ':protocol(http|https)/:hostAndPath(.+)/:sprintId',
},
- transformPath: ({ sprintId }) => `/jira/sprint/${sprintId}`,
- transformQueryParams: ({ protocol, hostAndPath }) => ({
- baseUrl: `${protocol}://${hostAndPath}`,
- }),
- dateAdded: new Date('2019-09-14'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
]
diff --git a/services/jira/jira-sprint-redirect.tester.js b/services/jira/jira-sprint-redirect.tester.js
index 7bcf30e33436f..eee8c39571b53 100644
--- a/services/jira/jira-sprint-redirect.tester.js
+++ b/services/jira/jira-sprint-redirect.tester.js
@@ -1,17 +1,12 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JiraSprintRedirect',
title: 'JiraSprintRedirect',
pathPrefix: '/jira/sprint',
-}))
+})
-t.create('jira sprint')
- .get('/https/jira.spring.io/94.svg')
- .expectRedirect(
- `/jira/sprint/94.svg?baseUrl=${encodeURIComponent(
- 'https://jira.spring.io'
- )}`
- )
+t.create('jira sprint').get('/https/jira.spring.io/94.json').expectBadge({
+ label: 'jira',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/jira/jira-sprint.service.js b/services/jira/jira-sprint.service.js
index 389b1b59ba086..66e31c43b8547 100644
--- a/services/jira/jira-sprint.service.js
+++ b/services/jira/jira-sprint.service.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService } = require('..')
-const { authConfig } = require('./jira-common')
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { authConfig } from './jira-common.js'
const queryParamSchema = Joi.object({
- baseUrl: optionalUrl.required(),
+ baseUrl: url,
}).required()
const schema = Joi.object({
@@ -19,58 +17,49 @@ const schema = Joi.object({
name: Joi.string(),
}).allow(null),
}).required(),
- })
+ }),
)
.required(),
}).required()
-const documentation = `
-
- To get the Sprint ID, go to your Backlog view in your project,
- right click on your sprint name and get the value of
- data-sprint-id.
-
+const description = `
+To get the \`Sprint ID\`, go to your Backlog view in your project,
+right click on your sprint name and get the value of
+\`data-sprint-id\`.
`
-module.exports = class JiraSprint extends BaseJsonService {
- static get category() {
- return 'issue-tracking'
- }
+export default class JiraSprint extends BaseJsonService {
+ static category = 'issue-tracking'
- static get route() {
- return {
- base: 'jira/sprint',
- pattern: ':sprintId',
- queryParamSchema,
- }
+ static route = {
+ base: 'jira/sprint',
+ pattern: ':sprintId',
+ queryParamSchema,
}
- static get auth() {
- return authConfig
- }
+ static auth = authConfig
- static get examples() {
- return [
- {
- title: 'JIRA sprint completion',
- namedParams: {
- sprintId: '94',
- },
- queryParams: {
- baseUrl: 'https://jira.spring.io',
- },
- staticPreview: this.render({
- numCompletedIssues: 27,
- numTotalIssues: 28,
- }),
- documentation,
+ static openApi = {
+ '/jira/sprint/{sprintId}': {
+ get: {
+ summary: 'JIRA sprint completion',
+ description,
+ parameters: [
+ pathParam({
+ name: 'sprintId',
+ example: '94',
+ }),
+ queryParam({
+ name: 'baseUrl',
+ example: 'https://issues.apache.org/jira',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'jira' }
- }
+ static defaultBadgeData = { label: 'jira' }
static render({ numCompletedIssues, numTotalIssues }) {
const percentComplete = numTotalIssues
@@ -98,17 +87,17 @@ module.exports = class JiraSprint extends BaseJsonService {
url: `${baseUrl}/rest/api/2/search`,
schema,
options: {
- qs: {
+ searchParams: {
jql: `sprint=${sprintId} AND type IN (Bug,Improvement,Story,"Technical task")`,
fields: 'resolution',
maxResults: 500,
},
},
- errorMessages: {
+ httpErrors: {
400: 'sprint not found',
404: 'sprint not found',
},
- })
+ }),
)
const numTotalIssues = json.total
@@ -116,6 +105,7 @@ module.exports = class JiraSprint extends BaseJsonService {
if (issue.fields.resolution != null) {
return issue.fields.resolution.name !== 'Unresolved'
}
+ return false
}).length
return this.constructor.render({ numTotalIssues, numCompletedIssues })
diff --git a/services/jira/jira-sprint.spec.js b/services/jira/jira-sprint.spec.js
index d9696f3e8b780..7ec69e9a6154a 100644
--- a/services/jira/jira-sprint.spec.js
+++ b/services/jira/jira-sprint.spec.js
@@ -1,51 +1,22 @@
-'use strict'
+import { testAuth } from '../test-helpers.js'
+import JiraSprint from './jira-sprint.service.js'
+import { config } from './jira-test-helpers.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const JiraSprint = require('./jira-sprint.service')
-const {
- user,
- pass,
- host,
- config,
- sprintId,
- sprintQueryString,
-} = require('./jira-test-helpers')
-
-describe('JiraSprint', function() {
- cleanUpNockAfterEach()
-
- it('sends the auth information as configured', async function() {
- const scope = nock(`https://${host}`)
- .get('/jira/rest/api/2/search')
- .query(sprintQueryString)
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, {
- total: 2,
- issues: [
- { fields: { resolution: { name: 'done' } } },
- { fields: { resolution: { name: 'Unresolved' } } },
- ],
- })
-
- expect(
- await JiraSprint.invoke(
- defaultContext,
- config,
+describe('JiraSprint', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ JiraSprint,
+ 'BasicAuth',
{
- sprintId,
+ total: 2,
+ issues: [
+ { fields: { resolution: { name: 'done' } } },
+ { fields: { resolution: { name: 'Unresolved' } } },
+ ],
},
- { baseUrl: `https://${host}/jira` }
+ { configOverride: config },
)
- ).to.deep.equal({
- label: 'completion',
- message: '50%',
- color: 'orange',
})
-
- scope.done()
})
})
diff --git a/services/jira/jira-sprint.tester.js b/services/jira/jira-sprint.tester.js
index fe817f6344d3f..9744c247e7522 100644
--- a/services/jira/jira-sprint.tester.js
+++ b/services/jira/jira-sprint.tester.js
@@ -1,24 +1,23 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isIntegerPercentage } = require('../test-validators')
-const { sprintId, sprintQueryString } = require('./jira-test-helpers')
+import { createServiceTester } from '../tester.js'
+import { isIntegerPercentage } from '../test-validators.js'
+import { sprintId, sprintQueryString } from './jira-test-helpers.js'
+export const t = await createServiceTester()
t.create('unknown sprint')
- .get('/abc.json?baseUrl=https://jira.spring.io')
+ .get('/abc.json?baseUrl=https://issues.apache.org/jira')
.expectBadge({ label: 'jira', message: 'sprint not found' })
t.create('known sprint')
- .get('/94.json?baseUrl=https://jira.spring.io')
+ .get('/3.json?baseUrl=https://issues.apache.org/jira')
.expectBadge({
label: 'completion',
message: isIntegerPercentage,
})
t.create('100% completion')
- .get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
+ .get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
- nock('http://issues.apache.org/jira/rest/api/2')
+ nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {
@@ -39,7 +38,7 @@ t.create('100% completion')
},
},
],
- })
+ }),
)
.expectBadge({
label: 'completion',
@@ -48,9 +47,9 @@ t.create('100% completion')
})
t.create('0% completion')
- .get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
+ .get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
- nock('http://issues.apache.org/jira/rest/api/2')
+ nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {
@@ -64,7 +63,7 @@ t.create('0% completion')
},
},
],
- })
+ }),
)
.expectBadge({
label: 'completion',
@@ -73,15 +72,15 @@ t.create('0% completion')
})
t.create('no issues in sprint')
- .get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
+ .get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
- nock('http://issues.apache.org/jira/rest/api/2')
+ nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {
total: 0,
issues: [],
- })
+ }),
)
.expectBadge({
label: 'completion',
@@ -111,7 +110,7 @@ t.create('issue with null resolution value')
},
},
],
- })
+ }),
)
.expectBadge({
label: 'completion',
diff --git a/services/jira/jira-test-helpers.js b/services/jira/jira-test-helpers.js
index 11c01cff674c9..6cdcdfff8919f 100644
--- a/services/jira/jira-test-helpers.js
+++ b/services/jira/jira-test-helpers.js
@@ -1,5 +1,3 @@
-'use strict'
-
const sprintId = 8
const sprintQueryString = {
jql: `sprint=${sprintId} AND type IN (Bug,Improvement,Story,"Technical task")`,
@@ -7,25 +5,14 @@ const sprintQueryString = {
maxResults: 500,
}
-const user = 'admin'
-const pass = 'password'
-const host = 'myprivatejira.test'
const config = {
public: {
services: {
jira: {
- authorizedOrigins: [`https://${host}`],
+ authorizedOrigins: ['https://issues.apache.org'],
},
},
},
- private: { jira_user: user, jira_pass: pass },
}
-module.exports = {
- sprintId,
- sprintQueryString,
- user,
- pass,
- host,
- config,
-}
+export { sprintId, sprintQueryString, config }
diff --git a/services/jitpack/jitpack-downloads.service.js b/services/jitpack/jitpack-downloads.service.js
deleted file mode 100644
index c3d25e1bcd8bc..0000000000000
--- a/services/jitpack/jitpack-downloads.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- route: {
- base: 'jitpack',
- pattern: ':interval(dw|dm)/:various*',
- },
- label: 'jitpack',
- category: 'downloads',
- dateAdded: new Date('2020-04-05'),
-})
diff --git a/services/jitpack/jitpack-downloads.tester.js b/services/jitpack/jitpack-downloads.tester.js
deleted file mode 100644
index 2aa58b9fb70e0..0000000000000
--- a/services/jitpack/jitpack-downloads.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'JitPackDownloads',
- title: 'JitPackDownloads',
- pathPrefix: '/jitpack',
-}))
-
-t.create('no longer available (dw)')
- .get('/dw/github/jitpack/maven-simple.json')
- .expectBadge({
- label: 'jitpack',
- message: 'no longer available',
- })
-
-t.create('no longer available (dm)')
- .get('/dm/github/jitpack/maven-simple.json')
- .expectBadge({
- label: 'jitpack',
- message: 'no longer available',
- })
diff --git a/services/jitpack/jitpack-version-redirector.service.js b/services/jitpack/jitpack-version-redirector.service.js
index 9fc943c3293fc..e2c23fafe5f3c 100644
--- a/services/jitpack/jitpack-version-redirector.service.js
+++ b/services/jitpack/jitpack-version-redirector.service.js
@@ -1,16 +1,26 @@
-'use strict'
+import { retiredService, redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
+ retiredService({
+ category: 'version',
+ label: 'jitpack',
+ name: 'JitpackVersionGitHubRedirect',
+ route: {
+ base: 'jitpack/v',
+ pattern: ':user/:repo',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+ }),
redirector({
category: 'version',
+ name: 'JitpackVersionVcsRedirect',
route: {
base: 'jitpack/v',
- pattern: ':groupId/:artifactId',
+ pattern: ':vcs(github|bitbucket|gitlab|gitee)/:user/:repo',
},
- transformPath: ({ groupId, artifactId }) =>
- `/jitpack/v/github/${groupId}/${artifactId}`,
- dateAdded: new Date('2019-03-31'),
+ transformPath: ({ vcs, user, repo }) =>
+ `/jitpack/version/com.${vcs}.${user}/${repo}`,
+ dateAdded: new Date('2022-08-21'),
}),
]
diff --git a/services/jitpack/jitpack-version-redirector.tester.js b/services/jitpack/jitpack-version-redirector.tester.js
index 9e19c99e117a2..149f4690661bf 100644
--- a/services/jitpack/jitpack-version-redirector.tester.js
+++ b/services/jitpack/jitpack-version-redirector.tester.js
@@ -1,13 +1,18 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'JitpackVersionRedirect',
title: 'JitpackVersionRedirect',
pathPrefix: '/jitpack/v',
-}))
+})
+
+t.create('jitpack version deprecated (no vcs)')
+ .get('/jitpack/maven-simple.json')
+ .expectBadge({
+ label: 'jitpack',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
-t.create('jitpack version redirect')
- .get('/jitpack/maven-simple.svg')
- .expectRedirect('/jitpack/v/github/jitpack/maven-simple.svg')
+t.create('jitpack version redirect (github)')
+ .get('/github/jitpack/maven-simple.svg')
+ .expectRedirect('/jitpack/version/com.github.jitpack/maven-simple.svg')
diff --git a/services/jitpack/jitpack-version.service.js b/services/jitpack/jitpack-version.service.js
index f4d7464b76848..136211e82abbd 100644
--- a/services/jitpack/jitpack-version.service.js
+++ b/services/jitpack/jitpack-version.service.js
@@ -1,59 +1,54 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
version: Joi.string().required(),
- status: Joi.string()
- .valid('ok')
- .required(),
+ status: Joi.string().valid('ok').required(),
}).required()
-module.exports = class JitPackVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
+export default class JitPackVersion extends BaseJsonService {
+ static category = 'version'
- static get route() {
- return {
- base: 'jitpack/v',
- pattern: ':vcs(github|bitbucket|gitlab|gitee)/:user/:repo',
- }
+ // Changed endpoint to allow any groupId, custom domains included
+ // See: https://github.com/badges/shields/issues/8312
+ static route = {
+ base: 'jitpack/version',
+ pattern: ':groupId/:artifactId',
}
- static get examples() {
- return [
- {
- title: 'JitPack',
- namedParams: {
- vcs: 'github',
- user: 'jitpack',
- repo: 'maven-simple',
- },
- staticPreview: renderVersionBadge({ version: 'v1.1' }),
- keywords: ['java', 'maven'],
+ static openApi = {
+ '/jitpack/version/{groupId}/{artifactId}': {
+ get: {
+ summary: 'JitPack',
+ parameters: pathParams(
+ {
+ name: 'groupId',
+ example: 'com.github.jitpack',
+ },
+ {
+ name: 'artifactId',
+ example: 'maven-simple',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'jitpack' }
- }
+ static defaultBadgeData = { label: 'jitpack' }
- async fetch({ vcs, user, repo }) {
- const url = `https://jitpack.io/api/builds/com.${vcs}.${user}/${repo}/latest`
+ async fetch({ groupId, artifactId }) {
+ const url = `https://jitpack.io/api/builds/${groupId}/${artifactId}/latestOk`
return this._requestJson({
schema,
url,
- errorMessages: { 401: 'project not found or private' },
+ httpErrors: { 401: 'project not found or private' },
})
}
- async handle({ vcs, user, repo }) {
- const { version } = await this.fetch({ vcs, user, repo })
+ async handle({ groupId, artifactId }) {
+ const { version } = await this.fetch({ groupId, artifactId })
return renderVersionBadge({ version })
}
}
diff --git a/services/jitpack/jitpack-version.tester.js b/services/jitpack/jitpack-version.tester.js
index 1debcd801b1b4..3f30f9ce9bf44 100644
--- a/services/jitpack/jitpack-version.tester.js
+++ b/services/jitpack/jitpack-version.tester.js
@@ -1,15 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// Github allows versions with chars, etc.
const isAnyV = Joi.string().regex(/^v.+$/)
t.create('version (groupId)')
- .get('/github/erayerdin/kappdirs.json')
+ .get('/com.github.erayerdin/kappdirs.json')
.expectBadge({ label: 'jitpack', message: isAnyV })
t.create('unknown package')
- .get('/github/some-bogus-user/project.json')
+ .get('/com.github.some-bogus-user/project.json')
.expectBadge({ label: 'jitpack', message: 'project not found or private' })
diff --git a/services/jsdelivr/jsdelivr-base.js b/services/jsdelivr/jsdelivr-base.js
index c8a78069acab5..f021b3997af37 100644
--- a/services/jsdelivr/jsdelivr-base.js
+++ b/services/jsdelivr/jsdelivr-base.js
@@ -1,9 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { downloadCount } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
total: Joi.number().required(),
@@ -17,22 +14,15 @@ const periodMap = {
}
class BaseJsDelivrService extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get defaultBadgeData() {
- return {
- label: 'jsdelivr',
- }
+ static defaultBadgeData = {
+ label: 'jsdelivr',
}
- static render({ period, hits }) {
- return {
- message: `${metric(hits)}/${periodMap[period]}`,
- color: downloadCount(hits),
- }
+ static render({ period, hits: downloads }) {
+ return renderDownloadsBadge({ downloads, interval: periodMap[period] })
}
}
-module.exports = { schema, periodMap, BaseJsDelivrService }
+export { schema, periodMap, BaseJsDelivrService }
diff --git a/services/jsdelivr/jsdelivr-hits-github.service.js b/services/jsdelivr/jsdelivr-hits-github.service.js
index 881f99f034e1f..0575492487ec5 100644
--- a/services/jsdelivr/jsdelivr-hits-github.service.js
+++ b/services/jsdelivr/jsdelivr-hits-github.service.js
@@ -1,27 +1,34 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { schema, periodMap, BaseJsDelivrService } from './jsdelivr-base.js'
-const { schema, periodMap, BaseJsDelivrService } = require('./jsdelivr-base')
-
-module.exports = class JsDelivrHitsGitHub extends BaseJsDelivrService {
- static get route() {
- return {
- base: 'jsdelivr/gh',
- pattern: ':period(hd|hw|hm|hy)/:user/:repo',
- }
+export default class JsDelivrHitsGitHub extends BaseJsDelivrService {
+ static route = {
+ base: 'jsdelivr/gh',
+ pattern: ':period(hd|hw|hm|hy)/:user/:repo',
}
- static get examples() {
- return [
- {
- title: 'jsDelivr hits (GitHub)',
- namedParams: {
- period: 'hm',
- user: 'jquery',
- repo: 'jquery',
- },
- staticPreview: this.render({ period: 'hm', hits: 9809876 }),
+ static openApi = {
+ '/jsdelivr/gh/{period}/{user}/{repo}': {
+ get: {
+ summary: 'jsDelivr hits (GitHub)',
+ parameters: pathParams(
+ {
+ name: 'period',
+ example: 'hm',
+ schema: { type: 'string', enum: this.getEnum('period') },
+ description: 'Hits per Day, Week, Month or Year',
+ },
+ {
+ name: 'user',
+ example: 'jquery',
+ },
+ {
+ name: 'repo',
+ example: 'jquery',
+ },
+ ),
},
- ]
+ },
}
async fetch({ period, user, repo }) {
diff --git a/services/jsdelivr/jsdelivr-hits-github.tester.js b/services/jsdelivr/jsdelivr-hits-github.tester.js
index 3c15ff5d0a22d..319b3ef99f685 100644
--- a/services/jsdelivr/jsdelivr-hits-github.tester.js
+++ b/services/jsdelivr/jsdelivr-hits-github.tester.js
@@ -1,14 +1,22 @@
-'use strict'
+import Joi from 'joi'
+import {
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+const isDownloadsOverTimePeriod = Joi.alternatives().try(
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+)
t.create('jquery/jquery hits/day')
.timeout(10000)
.get('/hd/jquery/jquery.json')
.expectBadge({
label: 'jsdelivr',
- message: isMetricOverTimePeriod,
+ message: isDownloadsOverTimePeriod,
})
t.create('jquery/jquery hits/week')
@@ -16,7 +24,7 @@ t.create('jquery/jquery hits/week')
.get('/hw/jquery/jquery.json')
.expectBadge({
label: 'jsdelivr',
- message: isMetricOverTimePeriod,
+ message: isDownloadsOverTimePeriod,
})
t.create('jquery/jquery hits/month')
diff --git a/services/jsdelivr/jsdelivr-hits-npm.service.js b/services/jsdelivr/jsdelivr-hits-npm.service.js
index 87af8aca1199c..86f5383229c27 100644
--- a/services/jsdelivr/jsdelivr-hits-npm.service.js
+++ b/services/jsdelivr/jsdelivr-hits-npm.service.js
@@ -1,37 +1,51 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { schema, periodMap, BaseJsDelivrService } from './jsdelivr-base.js'
-const { schema, periodMap, BaseJsDelivrService } = require('./jsdelivr-base')
-
-module.exports = class JsDelivrHitsNPM extends BaseJsDelivrService {
- static get route() {
- return {
- base: 'jsdelivr/npm',
- pattern: ':period(hd|hw|hm|hy)/:scope(@[^/]+)?/:packageName',
- }
+export default class JsDelivrHitsNPM extends BaseJsDelivrService {
+ static route = {
+ base: 'jsdelivr/npm',
+ pattern: ':period(hd|hw|hm|hy)/:scope(@[^/]+)?/:packageName',
}
- static get examples() {
- return [
- {
- title: 'jsDelivr hits (npm)',
- pattern: ':period(hd|hw|hm|hy)/:packageName',
- namedParams: {
- period: 'hm',
- packageName: 'jquery',
- },
- staticPreview: this.render({ period: 'hm', hits: 920101789 }),
+ static openApi = {
+ '/jsdelivr/npm/{period}/{packageName}': {
+ get: {
+ summary: 'jsDelivr hits (npm)',
+ parameters: pathParams(
+ {
+ name: 'period',
+ schema: { type: 'string', enum: this.getEnum('period') },
+ example: 'hm',
+ description: 'Hits per Day, Week, Month or Year',
+ },
+ {
+ name: 'packageName',
+ example: 'fire',
+ },
+ ),
},
- {
- title: 'jsDelivr hits (npm scoped)',
- pattern: ':period(hd|hw|hm|hy)/:scope?/:packageName',
- namedParams: {
- period: 'hm',
- scope: '@angular',
- packageName: 'fire',
- },
- staticPreview: this.render({ period: 'hm', hits: 94123 }),
+ },
+ '/jsdelivr/npm/{period}/{scope}/{packageName}': {
+ get: {
+ summary: 'jsDelivr hits (npm scoped)',
+ parameters: pathParams(
+ {
+ name: 'period',
+ schema: { type: 'string', enum: this.getEnum('period') },
+ example: 'hm',
+ description: 'Hits per Day, Week, Month or Year',
+ },
+ {
+ name: 'scope',
+ example: '@angular',
+ },
+ {
+ name: 'packageName',
+ example: 'fire',
+ },
+ ),
},
- ]
+ },
}
async fetch({ period, packageName }) {
diff --git a/services/jsdelivr/jsdelivr-hits-npm.tester.js b/services/jsdelivr/jsdelivr-hits-npm.tester.js
index f1ce84c12e6ae..747c08ef464b9 100644
--- a/services/jsdelivr/jsdelivr-hits-npm.tester.js
+++ b/services/jsdelivr/jsdelivr-hits-npm.tester.js
@@ -1,39 +1,35 @@
-'use strict'
+import Joi from 'joi'
+import {
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+} from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+const isDownloadsOverTimePeriod = Joi.alternatives().try(
+ isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
+)
-t.create('jquery hits/day')
- .timeout(10000)
- .get('/hd/ky.json')
- .expectBadge({
- label: 'jsdelivr',
- message: isMetricOverTimePeriod,
- })
+t.create('jquery hits/day').timeout(10000).get('/hd/ky.json').expectBadge({
+ label: 'jsdelivr',
+ message: isDownloadsOverTimePeriod,
+})
-t.create('jquery hits/week')
- .timeout(10000)
- .get('/hw/ky.json')
- .expectBadge({
- label: 'jsdelivr',
- message: isMetricOverTimePeriod,
- })
+t.create('jquery hits/week').timeout(10000).get('/hw/ky.json').expectBadge({
+ label: 'jsdelivr',
+ message: isDownloadsOverTimePeriod,
+})
-t.create('jquery hits/month')
- .timeout(10000)
- .get('/hm/ky.json')
- .expectBadge({
- label: 'jsdelivr',
- message: isMetricOverTimePeriod,
- })
+t.create('jquery hits/month').timeout(10000).get('/hm/ky.json').expectBadge({
+ label: 'jsdelivr',
+ message: isMetricOverTimePeriod,
+})
-t.create('jquery hits/year')
- .timeout(25000)
- .get('/hy/ky.json')
- .expectBadge({
- label: 'jsdelivr',
- message: isMetricOverTimePeriod,
- })
+t.create('jquery hits/year').timeout(25000).get('/hy/ky.json').expectBadge({
+ label: 'jsdelivr',
+ message: isMetricOverTimePeriod,
+})
t.create('fake package')
.timeout(10000)
diff --git a/services/jsr/jsr-version.service.js b/services/jsr/jsr-version.service.js
new file mode 100644
index 0000000000000..4cb30200289af
--- /dev/null
+++ b/services/jsr/jsr-version.service.js
@@ -0,0 +1,60 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ latest: Joi.string().required(),
+}).required()
+
+export default class JsrVersion extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'jsr/v',
+ pattern: ':scope(@[^/]+)/:packageName',
+ }
+
+ static openApi = {
+ '/jsr/v/{scope}/{packageName}': {
+ get: {
+ summary: 'JSR Version',
+ description:
+ '[JSR](https://jsr.io/) is a modern package registry for JavaScript and TypeScript.',
+ parameters: pathParams(
+ {
+ name: 'scope',
+ example: '@luca',
+ },
+ {
+ name: 'packageName',
+ example: 'flag',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'jsr',
+ }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async fetch({ scope, packageName }) {
+ // see https://jsr.io/docs/api#package-version-metadata
+ return this._requestJson({
+ schema,
+ url: `https://jsr.io/${scope}/${packageName}/meta.json`,
+ httpErrors: {
+ 404: 'package not found',
+ },
+ })
+ }
+
+ async handle({ scope, packageName }) {
+ const { latest } = await this.fetch({ scope, packageName })
+ return this.constructor.render({ version: latest })
+ }
+}
diff --git a/services/jsr/jsr-version.tester.js b/services/jsr/jsr-version.tester.js
new file mode 100644
index 0000000000000..877f635c505d4
--- /dev/null
+++ b/services/jsr/jsr-version.tester.js
@@ -0,0 +1,15 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('gets the version of @luca/flag')
+ .get('/@luca/flag.json')
+ .expectBadge({ label: 'jsr', message: isSemver })
+
+t.create('gets the version of @std/assert')
+ .get('/@std/assert.json')
+ .expectBadge({ label: 'jsr', message: isSemver })
+
+t.create('returns an error when getting a non-existent')
+ .get('/@std/this-is-a-non-existent-package-name.json')
+ .expectBadge({ label: 'jsr', message: 'package not found' })
diff --git a/services/keybase/keybase-btc.service.js b/services/keybase/keybase-btc.service.js
index fa8b8e4210e77..811f00dd8c56e 100644
--- a/services/keybase/keybase-btc.service.js
+++ b/services/keybase/keybase-btc.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const KeybaseProfile = require('./keybase-profile')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import KeybaseProfile from './keybase-profile.js'
const bitcoinAddressSchema = Joi.object({
status: Joi.object({
@@ -15,60 +14,55 @@ const bitcoinAddressSchema = Joi.object({
bitcoin: Joi.array().items(
Joi.object({
address: Joi.string().required(),
- }).required()
+ }).required(),
),
})
.required()
.allow(null),
})
.required()
- .allow(null)
+ .allow(null),
)
.min(0)
.max(1),
}).required()
-module.exports = class KeybaseBTC extends KeybaseProfile {
- static get route() {
- return {
- base: 'keybase/btc',
- pattern: ':username',
- }
+export default class KeybaseBTC extends KeybaseProfile {
+ static route = {
+ base: 'keybase/btc',
+ pattern: ':username',
}
- static get examples() {
- return [
- {
- title: 'Keybase BTC',
- namedParams: { username: 'skyplabs' },
- staticPreview: this.render({
- address: '12ufRLmbEmgjsdGzhUUFY4pcfiQZyRPV9J',
+ static openApi = {
+ '/keybase/btc/{username}': {
+ get: {
+ summary: 'Keybase BTC',
+ parameters: pathParams({
+ name: 'username',
+ example: 'skyplabs',
}),
- keywords: ['bitcoin'],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'btc',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'btc',
+ color: 'informational',
+ namedLogo: 'keybase',
}
static render({ address }) {
return {
message: address,
+ style: 'social',
}
}
- static get apiVersion() {
- return '1.0'
- }
+ static apiVersion = '1.0'
async handle({ username }) {
const options = {
- form: {
+ searchParams: {
usernames: username,
fields: 'cryptocurrency_addresses',
},
diff --git a/services/keybase/keybase-btc.tester.js b/services/keybase/keybase-btc.tester.js
index 325203385b060..01e4531ad8d68 100644
--- a/services/keybase/keybase-btc.tester.js
+++ b/services/keybase/keybase-btc.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('existing bitcoin address')
.get('/skyplabs.json')
@@ -10,23 +9,17 @@ t.create('existing bitcoin address')
message: withRegex(/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/),
})
-t.create('unknown username')
- .get('/skyplabsssssss.json')
- .expectBadge({
- label: 'btc',
- message: 'profile not found',
- })
+t.create('unknown username').get('/skyplabsssssss.json').expectBadge({
+ label: 'btc',
+ message: 'profile not found',
+})
-t.create('invalid username')
- .get('/s.json')
- .expectBadge({
- label: 'btc',
- message: 'invalid username',
- })
+t.create('invalid username').get('/s.json').expectBadge({
+ label: 'btc',
+ message: 'invalid username',
+})
-t.create('missing bitcoin address')
- .get('/test.json')
- .expectBadge({
- label: 'btc',
- message: 'no bitcoin addresses found',
- })
+t.create('missing bitcoin address').get('/test.json').expectBadge({
+ label: 'btc',
+ message: 'no bitcoin addresses found',
+})
diff --git a/services/keybase/keybase-pgp.service.js b/services/keybase/keybase-pgp.service.js
index 4cd402f532bde..c70b9e73b48ca 100644
--- a/services/keybase/keybase-pgp.service.js
+++ b/services/keybase/keybase-pgp.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const KeybaseProfile = require('./keybase-profile')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import KeybaseProfile from './keybase-profile.js'
const keyFingerprintSchema = Joi.object({
status: Joi.object({
@@ -13,57 +12,53 @@ const keyFingerprintSchema = Joi.object({
Joi.object({
public_keys: {
primary: {
- key_fingerprint: Joi.string()
- .hex()
- .required(),
+ key_fingerprint: Joi.string().hex().required(),
},
},
})
.required()
- .allow(null)
+ .allow(null),
)
.min(0)
.max(1),
}).required()
-module.exports = class KeybasePGP extends KeybaseProfile {
- static get route() {
- return {
- base: 'keybase/pgp',
- pattern: ':username',
- }
+export default class KeybasePGP extends KeybaseProfile {
+ static route = {
+ base: 'keybase/pgp',
+ pattern: ':username',
}
- static get examples() {
- return [
- {
- title: 'Keybase PGP',
- namedParams: { username: 'skyplabs' },
- staticPreview: this.render({ fingerprint: '1863145FD39EE07E' }),
+ static openApi = {
+ '/keybase/pgp/{username}': {
+ get: {
+ summary: 'Keybase PGP',
+ parameters: pathParams({
+ name: 'username',
+ example: 'skyplabs',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'pgp',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'pgp',
+ color: 'informational',
+ namedLogo: 'keybase',
}
static render({ fingerprint }) {
return {
message: fingerprint.slice(-16).toUpperCase(),
+ style: 'social',
}
}
- static get apiVersion() {
- return '1.0'
- }
+ static apiVersion = '1.0'
async handle({ username }) {
const options = {
- form: {
+ searchParams: {
usernames: username,
fields: 'public_keys',
},
diff --git a/services/keybase/keybase-pgp.tester.js b/services/keybase/keybase-pgp.tester.js
index 40d90d0a355c7..4a964597d2fa2 100644
--- a/services/keybase/keybase-pgp.tester.js
+++ b/services/keybase/keybase-pgp.tester.js
@@ -1,34 +1,25 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('existing key fingerprint')
.get('/skyplabs.json')
.expectBadge({
label: 'pgp',
- message: Joi.string()
- .hex()
- .length(16),
+ message: Joi.string().hex().length(16),
})
-t.create('unknown username')
- .get('/skyplabsssssss.json')
- .expectBadge({
- label: 'pgp',
- message: 'profile not found',
- })
+t.create('unknown username').get('/skyplabsssssss.json').expectBadge({
+ label: 'pgp',
+ message: 'profile not found',
+})
-t.create('invalid username')
- .get('/s.json')
- .expectBadge({
- label: 'pgp',
- message: 'invalid username',
- })
+t.create('invalid username').get('/s.json').expectBadge({
+ label: 'pgp',
+ message: 'invalid username',
+})
-t.create('missing key fingerprint')
- .get('/skyp.json')
- .expectBadge({
- label: 'pgp',
- message: 'no key fingerprint found',
- })
+t.create('missing key fingerprint').get('/skyp.json').expectBadge({
+ label: 'pgp',
+ message: 'no key fingerprint found',
+})
diff --git a/services/keybase/keybase-profile.js b/services/keybase/keybase-profile.js
index 5e784a53caaf3..1949c524578af 100644
--- a/services/keybase/keybase-profile.js
+++ b/services/keybase/keybase-profile.js
@@ -1,16 +1,11 @@
-'use strict'
+import { BaseJsonService, NotFound } from '../index.js'
-const { BaseJsonService } = require('..')
-const { NotFound } = require('..')
-
-module.exports = class KeybaseProfile extends BaseJsonService {
+export default class KeybaseProfile extends BaseJsonService {
static get apiVersion() {
throw new Error(`apiVersion() is not implemented for ${this.name}`)
}
- static get category() {
- return 'social'
- }
+ static category = 'social'
async fetch({ schema, options }) {
const apiVersion = this.constructor.apiVersion
diff --git a/services/keybase/keybase-xlm.service.js b/services/keybase/keybase-xlm.service.js
index f3051c4f0fe1d..23a2dbb870e7c 100644
--- a/services/keybase/keybase-xlm.service.js
+++ b/services/keybase/keybase-xlm.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const KeybaseProfile = require('./keybase-profile')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import KeybaseProfile from './keybase-profile.js'
const stellarAddressSchema = Joi.object({
status: Joi.object({
@@ -20,53 +19,48 @@ const stellarAddressSchema = Joi.object({
}).required(),
})
.required()
- .allow(null)
+ .allow(null),
)
.min(0)
.max(1),
}).required()
-module.exports = class KeybaseXLM extends KeybaseProfile {
- static get route() {
- return {
- base: 'keybase/xlm',
- pattern: ':username',
- }
+export default class KeybaseXLM extends KeybaseProfile {
+ static route = {
+ base: 'keybase/xlm',
+ pattern: ':username',
}
- static get examples() {
- return [
- {
- title: 'Keybase XLM',
- namedParams: { username: 'skyplabs' },
- staticPreview: this.render({
- address: 'GCGH37DYONEBPGAZGCHJEZZF3J2Q3EFYZBQBE6UJL5QKTULCMEA6MXLA',
+ static openApi = {
+ '/keybase/xlm/{username}': {
+ get: {
+ summary: 'Keybase XLM',
+ parameters: pathParams({
+ name: 'username',
+ example: 'skyplabs',
}),
- keywords: ['stellar'],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'xlm',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'xlm',
+ color: 'informational',
+ namedLogo: 'keybase',
}
static render({ address }) {
return {
message: address,
+ style: 'social',
}
}
- static get apiVersion() {
- return '1.0'
- }
+ static apiVersion = '1.0'
async handle({ username }) {
const options = {
- form: {
+ searchParams: {
usernames: username,
fields: 'stellar',
},
diff --git a/services/keybase/keybase-xlm.tester.js b/services/keybase/keybase-xlm.tester.js
index 839e8348a4020..ce8b78637cd65 100644
--- a/services/keybase/keybase-xlm.tester.js
+++ b/services/keybase/keybase-xlm.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('existing stellar address')
.get('/skyplabs.json')
@@ -10,23 +9,17 @@ t.create('existing stellar address')
message: withRegex(/^(?!not found$)/),
})
-t.create('unknown username')
- .get('/skyplabsssssss.json')
- .expectBadge({
- label: 'xlm',
- message: 'profile not found',
- })
+t.create('unknown username').get('/skyplabsssssss.json').expectBadge({
+ label: 'xlm',
+ message: 'profile not found',
+})
-t.create('invalid username')
- .get('/s.json')
- .expectBadge({
- label: 'xlm',
- message: 'invalid username',
- })
+t.create('invalid username').get('/s.json').expectBadge({
+ label: 'xlm',
+ message: 'invalid username',
+})
-t.create('missing stellar address')
- .get('/test.json')
- .expectBadge({
- label: 'xlm',
- message: 'no stellar address found',
- })
+t.create('missing stellar address').get('/test.json').expectBadge({
+ label: 'xlm',
+ message: 'no stellar address found',
+})
diff --git a/services/keybase/keybase-zec.service.js b/services/keybase/keybase-zec.service.js
index 442ab51859037..758774cb98a3d 100644
--- a/services/keybase/keybase-zec.service.js
+++ b/services/keybase/keybase-zec.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const KeybaseProfile = require('./keybase-profile')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import KeybaseProfile from './keybase-profile.js'
const zcachAddressSchema = Joi.object({
status: Joi.object({
@@ -15,60 +14,55 @@ const zcachAddressSchema = Joi.object({
zcash: Joi.array().items(
Joi.object({
address: Joi.string().required(),
- }).required()
+ }).required(),
),
})
.required()
.allow(null),
})
.required()
- .allow(null)
+ .allow(null),
)
.min(0)
.max(1),
}).required()
-module.exports = class KeybaseZEC extends KeybaseProfile {
- static get route() {
- return {
- base: 'keybase/zec',
- pattern: ':username',
- }
+export default class KeybaseZEC extends KeybaseProfile {
+ static route = {
+ base: 'keybase/zec',
+ pattern: ':username',
}
- static get examples() {
- return [
- {
- title: 'Keybase ZEC',
- namedParams: { username: 'skyplabs' },
- staticPreview: this.render({
- address: 't1RJDxpBcsgqAotqhepkhLFMv2XpMfvnf1y',
+ static openApi = {
+ '/keybase/zec/{username}': {
+ get: {
+ summary: 'Keybase ZEC',
+ parameters: pathParams({
+ name: 'username',
+ example: 'skyplabs',
}),
- keywords: ['zcash'],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'zec',
- color: 'informational',
- }
+ static defaultBadgeData = {
+ label: 'zec',
+ color: 'informational',
+ namedLogo: 'keybase',
}
static render({ address }) {
return {
message: address,
+ style: 'social',
}
}
- static get apiVersion() {
- return '1.0'
- }
+ static apiVersion = '1.0'
async handle({ username }) {
const options = {
- form: {
+ searchParams: {
usernames: username,
fields: 'cryptocurrency_addresses',
},
diff --git a/services/keybase/keybase-zec.tester.js b/services/keybase/keybase-zec.tester.js
index 7d6bfbd7dd611..f38a394116a0a 100644
--- a/services/keybase/keybase-zec.tester.js
+++ b/services/keybase/keybase-zec.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('existing zcash address')
.get('/skyplabs.json')
@@ -10,23 +9,17 @@ t.create('existing zcash address')
message: withRegex(/^(?!not found$)/),
})
-t.create('unknown username')
- .get('/skyplabsssssss.json')
- .expectBadge({
- label: 'zec',
- message: 'profile not found',
- })
+t.create('unknown username').get('/skyplabsssssss.json').expectBadge({
+ label: 'zec',
+ message: 'profile not found',
+})
-t.create('invalid username')
- .get('/s.json')
- .expectBadge({
- label: 'zec',
- message: 'invalid username',
- })
+t.create('invalid username').get('/s.json').expectBadge({
+ label: 'zec',
+ message: 'invalid username',
+})
-t.create('missing zcash address')
- .get('/test.json')
- .expectBadge({
- label: 'zec',
- message: 'no zcash addresses found',
- })
+t.create('missing zcash address').get('/test.json').expectBadge({
+ label: 'zec',
+ message: 'no zcash addresses found',
+})
diff --git a/services/leanpub/leanpub-book-summary.service.js b/services/leanpub/leanpub-book-summary.service.js
deleted file mode 100644
index 64ccbd37a0614..0000000000000
--- a/services/leanpub/leanpub-book-summary.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- route: {
- base: 'leanpub/book',
- pattern: ':various+',
- },
- category: 'funding',
- label: 'leanpub',
- dateAdded: new Date('2019-12-30'),
-})
diff --git a/services/leanpub/leanpub-book-summary.tester.js b/services/leanpub/leanpub-book-summary.tester.js
deleted file mode 100644
index 9ed01c2a0690c..0000000000000
--- a/services/leanpub/leanpub-book-summary.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'LeanPub',
- title: 'LeanPub',
- pathPrefix: '/leanpub/book',
-}))
-
-t.create('no longer available (previously book pages)')
- .get('/pages/juice-shop.json')
- .expectBadge({
- label: 'leanpub',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously books sold)')
- .get('/sold/juice-shop.json')
- .expectBadge({
- label: 'leanpub',
- message: 'no longer available',
- })
diff --git a/services/lemmy/lemmy.service.js b/services/lemmy/lemmy.service.js
new file mode 100644
index 0000000000000..dd9d2e2f3b310
--- /dev/null
+++ b/services/lemmy/lemmy.service.js
@@ -0,0 +1,74 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, InvalidParameter, pathParams } from '../index.js'
+
+const lemmyCommunitySchema = Joi.object({
+ community_view: Joi.object({
+ counts: Joi.object({
+ subscribers: Joi.number().required(),
+ }).required(),
+ }).required(),
+}).required()
+
+export default class Lemmy extends BaseJsonService {
+ static category = 'social'
+
+ static route = {
+ base: 'lemmy',
+ pattern: ':community',
+ }
+
+ static openApi = {
+ '/lemmy/{community}': {
+ get: {
+ summary: 'Lemmy',
+ parameters: pathParams({
+ name: 'community',
+ example: 'asklemmy@lemmy.ml',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'community', namedLogo: 'lemmy' }
+
+ static render({ community, members }) {
+ return {
+ label: `subscribe to ${community}`,
+ message: metric(members),
+ style: 'social',
+ color: 'brightgreen',
+ }
+ }
+
+ async fetch({ community }) {
+ const splitAlias = community.split('@')
+ // The community will be in the format of `community@server`
+ if (splitAlias.length !== 2) {
+ throw new InvalidParameter({
+ prettyMessage: 'invalid community',
+ })
+ }
+
+ const host = splitAlias[1]
+
+ const data = await this._requestJson({
+ url: `https://${host}/api/v3/community`,
+ schema: lemmyCommunitySchema,
+ options: {
+ searchParams: {
+ name: community,
+ },
+ },
+ httpErrors: {
+ 404: 'community not found',
+ },
+ })
+ return data.community_view.counts.subscribers
+ }
+
+ async handle({ community }) {
+ const members = await this.fetch({ community })
+ return this.constructor.render({ community, members })
+ }
+}
diff --git a/services/lemmy/lemmy.tester.js b/services/lemmy/lemmy.tester.js
new file mode 100644
index 0000000000000..b4b3ad2bfdc2d
--- /dev/null
+++ b/services/lemmy/lemmy.tester.js
@@ -0,0 +1,69 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('get community subscribers')
+ .get('/community@DUMMY.dumb.json')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb/')
+ .get('/api/v3/community?name=community%40DUMMY.dumb')
+ .reply(
+ 200,
+ JSON.stringify({
+ community_view: {
+ counts: {
+ subscribers: 42,
+ posts: 0,
+ comments: 0,
+ },
+ },
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'subscribe to community@DUMMY.dumb',
+ message: '42',
+ color: 'brightgreen',
+ })
+
+t.create('bad server or connection')
+ .get('/community@DUMMY.dumb.json')
+ .networkOff()
+ .expectBadge({
+ label: 'community',
+ message: 'inaccessible',
+ color: 'lightgrey',
+ })
+
+t.create('unknown community')
+ .get('/community@DUMMY.dumb.json')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb/')
+ .get('/api/v3/community?name=community%40DUMMY.dumb')
+ .reply(
+ 404,
+ JSON.stringify({
+ error: 'couldnt_find_community',
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'community',
+ message: 'community not found',
+ color: 'red',
+ })
+
+t.create('invalid community').get('/ALIASDUMMY.dumb.json').expectBadge({
+ label: 'community',
+ message: 'invalid community',
+ color: 'red',
+})
+
+t.create('test on real lemmy room for API compliance')
+ .get('/asklemmy@lemmy.ml.json')
+ .timeout(10000)
+ .expectBadge({
+ label: 'subscribe to asklemmy@lemmy.ml',
+ message: Joi.string().regex(/^[0-9]+k$/),
+ color: 'brightgreen',
+ })
diff --git a/services/lgtm/lgtm-alerts.service.js b/services/lgtm/lgtm-alerts.service.js
deleted file mode 100644
index 39a4c42518c54..0000000000000
--- a/services/lgtm/lgtm-alerts.service.js
+++ /dev/null
@@ -1,47 +0,0 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const LgtmBaseService = require('./lgtm-base')
-
-module.exports = class LgtmAlerts extends LgtmBaseService {
- static get route() {
- return {
- base: 'lgtm/alerts',
- pattern: this.pattern,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'LGTM Alerts',
- namedParams: {
- host: 'github',
- user: 'apache',
- repo: 'cloudstack',
- },
- staticPreview: this.render({ alerts: 2488 }),
- },
- ]
- }
-
- static getColor({ alerts }) {
- let color = 'yellow'
- if (alerts === 0) {
- color = 'brightgreen'
- }
- return color
- }
-
- static render({ alerts }) {
- return {
- message: metric(alerts) + (alerts === 1 ? ' alert' : ' alerts'),
- color: this.getColor({ alerts }),
- }
- }
-
- async handle({ host, user, repo }) {
- const { alerts } = await this.fetch({ host, user, repo })
- return this.constructor.render({ alerts })
- }
-}
diff --git a/services/lgtm/lgtm-alerts.tester.js b/services/lgtm/lgtm-alerts.tester.js
deleted file mode 100644
index 39b5f81f6a647..0000000000000
--- a/services/lgtm/lgtm-alerts.tester.js
+++ /dev/null
@@ -1,69 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { data } = require('./lgtm-test-helpers')
-
-t.create('alerts: total alerts for a project')
- .get('/github/apache/cloudstack.json')
- .expectBadge({
- label: 'lgtm',
- message: Joi.string().regex(/^[0-9kM.]+ alerts?$/),
- })
-
-t.create('alerts: missing project')
- .get('/github/some-org/this-project-doesnt-exist.json')
- .expectBadge({
- label: 'lgtm',
- message: 'project not found',
- })
-
-t.create('alerts: no alerts')
- .get('/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, { alerts: 0, languages: data.languages })
- )
- .expectBadge({ label: 'lgtm', message: '0 alerts' })
-
-t.create('alerts: single alert')
- .get('/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, { alerts: 1, languages: data.languages })
- )
- .expectBadge({ label: 'lgtm', message: '1 alert' })
-
-t.create('alerts: multiple alerts')
- .get('/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, { alerts: 123, languages: data.languages })
- )
- .expectBadge({ label: 'lgtm', message: '123 alerts' })
-
-t.create('alerts: json missing alerts')
- .get('/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, {})
- )
- .expectBadge({ label: 'lgtm', message: 'invalid response data' })
-
-t.create('alerts: total alerts for a project with a github mapped host')
- .get('/github/apache/cloudstack.json')
- .expectBadge({
- label: 'lgtm',
- message: Joi.string().regex(/^[0-9kM.]+ alerts?$/),
- })
-
-t.create('alerts: total alerts for a project with a bitbucket mapped host')
- .get('/bitbucket/atlassian/confluence-business-blueprints.json')
- .expectBadge({
- label: 'lgtm',
- message: Joi.string().regex(/^[0-9kM.]+ alerts?$/),
- })
diff --git a/services/lgtm/lgtm-base.js b/services/lgtm/lgtm-base.js
deleted file mode 100644
index 916675d77aa25..0000000000000
--- a/services/lgtm/lgtm-base.js
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- alerts: Joi.number().required(),
-
- languages: Joi.array()
- .items(
- Joi.object({
- lang: Joi.string().required(),
- grade: Joi.string(),
- })
- )
- .required(),
-}).required()
-
-const hostMappings = {
- github: 'g',
- bitbucket: 'b',
- gitlab: 'gl',
-}
-
-module.exports = class LgtmBaseService extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
-
- static get defaultBadgeData() {
- return { label: 'lgtm' }
- }
-
- static get pattern() {
- return `:host(${Object.keys(hostMappings).join('|')})/:user/:repo`
- }
-
- async fetch({ host, user, repo }) {
- const mappedHost = hostMappings[host]
- const url = `https://lgtm.com/api/v0.1/project/${mappedHost}/${user}/${repo}/details`
-
- return this._requestJson({
- schema,
- url,
- errorMessages: {
- 404: 'project not found',
- },
- })
- }
-}
diff --git a/services/lgtm/lgtm-grade.service.js b/services/lgtm/lgtm-grade.service.js
deleted file mode 100644
index 1f4f2830abcbe..0000000000000
--- a/services/lgtm/lgtm-grade.service.js
+++ /dev/null
@@ -1,94 +0,0 @@
-'use strict'
-
-const LgtmBaseService = require('./lgtm-base')
-
-module.exports = class LgtmGrade extends LgtmBaseService {
- static get route() {
- return {
- base: 'lgtm/grade',
- pattern: `:language/${this.pattern}`,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'LGTM Grade',
- namedParams: {
- language: 'java',
- host: 'github',
- user: 'apache',
- repo: 'cloudstack',
- },
- staticPreview: this.render({
- language: 'java',
- data: {
- languages: [
- {
- lang: 'java',
- grade: 'C',
- },
- ],
- },
- }),
- },
- ]
- }
-
- static getLabel({ language }) {
- const languageLabel = (() => {
- switch (language) {
- case 'cpp':
- return 'c/c++'
- case 'csharp':
- return 'c#'
- // Javascript analysis on LGTM also includes TypeScript
- case 'javascript':
- return 'js/ts'
- default:
- return language
- }
- })()
- return languageLabel
- }
-
- static getGradeAndColor({ language, data }) {
- let grade = 'no language data'
- let color = 'red'
-
- for (const languageData of data.languages) {
- if (languageData.lang === language && 'grade' in languageData) {
- // Pretty label for the language
- grade = languageData.grade
- // Pick colour based on grade
- if (languageData.grade === 'A+') {
- color = 'brightgreen'
- } else if (languageData.grade === 'A') {
- color = 'green'
- } else if (languageData.grade === 'B') {
- color = 'yellowgreen'
- } else if (languageData.grade === 'C') {
- color = 'yellow'
- } else if (languageData.grade === 'D') {
- color = 'orange'
- }
- }
- }
- return { grade, color }
- }
-
- static render({ language, data }) {
- const { grade, color } = this.getGradeAndColor({ language, data })
-
- return {
- label: `code quality: ${this.getLabel({ language })}`,
- message: grade,
- color,
- }
- }
-
- async handle({ language, host, user, repo }) {
- const data = await this.fetch({ host, user, repo })
- return this.constructor.render({ language, data })
- }
-}
diff --git a/services/lgtm/lgtm-grade.tester.js b/services/lgtm/lgtm-grade.tester.js
deleted file mode 100644
index 85b053fe35d0c..0000000000000
--- a/services/lgtm/lgtm-grade.tester.js
+++ /dev/null
@@ -1,107 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { data } = require('./lgtm-test-helpers')
-
-t.create('grade: missing project')
- .get('/java/github/some-org/this-project-doesnt-exist.json')
- .expectBadge({
- label: 'lgtm',
- message: 'project not found',
- })
-
-t.create('grade: json missing languages')
- .get('/java/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, {})
- )
- .expectBadge({ label: 'lgtm', message: 'invalid response data' })
-
-t.create('grade: grade for a project (java)')
- .get('/java/github/apache/cloudstack.json')
- .expectBadge({
- label: 'code quality: java',
- message: Joi.string().regex(/^(?:A\+)|A|B|C|D|E$/),
- })
-
-t.create('grade: grade for missing language')
- .get('/foo/github/apache/cloudstack.json')
- .expectBadge({
- label: 'code quality: foo',
- message: 'no language data',
- })
-
-t.create('grade: grade for a project with a mapped host')
- .get('/java/github/apache/cloudstack.json')
- .expectBadge({
- label: 'code quality: java',
- message: Joi.string().regex(/^(?:A\+)|A|B|C|D|E$/),
- })
-
-// Test display of languages
-
-t.create('grade: cpp')
- .get('/cpp/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: c/c++', message: 'A+' })
-
-t.create('grade: javascript')
- .get('/javascript/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: js/ts', message: 'A' })
-
-t.create('grade: java')
- .get('/java/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: java', message: 'B' })
-
-t.create('grade: python')
- .get('/python/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: python', message: 'C' })
-
-t.create('grade: csharp')
- .get('/csharp/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: c#', message: 'D' })
-
-t.create('grade: other')
- .get('/other/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: other', message: 'E' })
-
-t.create('grade: foo (no grade for valid language)')
- .get('/foo/github/apache/cloudstack.json')
- .intercept(nock =>
- nock('https://lgtm.com')
- .get('/api/v0.1/project/g/apache/cloudstack/details')
- .reply(200, data)
- )
- .expectBadge({ label: 'code quality: foo', message: 'no language data' })
diff --git a/services/lgtm/lgtm-redirector.service.js b/services/lgtm/lgtm-redirector.service.js
deleted file mode 100644
index b88b3778ee323..0000000000000
--- a/services/lgtm/lgtm-redirector.service.js
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-const commonAttrs = {
- category: 'analysis',
- dateAdded: new Date('2019-04-30'),
-}
-
-module.exports = [
- redirector({
- route: {
- base: 'lgtm/alerts/g',
- pattern: ':user/:repo',
- },
- transformPath: ({ user, repo }) => `/lgtm/alerts/github/${user}/${repo}`,
- ...commonAttrs,
- }),
- redirector({
- route: {
- base: 'lgtm/grade',
- pattern: ':language/g/:user/:repo',
- },
- transformPath: ({ language, user, repo }) =>
- `/lgtm/grade/${language}/github/${user}/${repo}`,
- ...commonAttrs,
- }),
-]
diff --git a/services/lgtm/lgtm-redirector.tester.js b/services/lgtm/lgtm-redirector.tester.js
deleted file mode 100644
index 449955ad9e391..0000000000000
--- a/services/lgtm/lgtm-redirector.tester.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'LgtmRedirect',
- title: 'LgtmRedirect',
- pathPrefix: '/lgtm',
-}))
-
-t.create('alerts')
- .get('/alerts/g/badges/shields.svg')
- .expectRedirect('/lgtm/alerts/github/badges/shields.svg')
-
-t.create('grade')
- .get('/grade/java/g/apache/cloudstack.svg')
- .expectRedirect('/lgtm/grade/java/github/apache/cloudstack.svg')
diff --git a/services/lgtm/lgtm-test-helpers.js b/services/lgtm/lgtm-test-helpers.js
deleted file mode 100644
index 71154a409a957..0000000000000
--- a/services/lgtm/lgtm-test-helpers.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict'
-
-const data = {
- alerts: 0,
- languages: [
- { lang: 'cpp', grade: 'A+' },
- { lang: 'javascript', grade: 'A' },
- { lang: 'java', grade: 'B' },
- { lang: 'python', grade: 'C' },
- { lang: 'csharp', grade: 'D' },
- { lang: 'other', grade: 'E' },
- { lang: 'foo' },
- ],
-}
-
-module.exports = {
- data,
-}
diff --git a/services/liberapay/liberapay-base.js b/services/liberapay/liberapay-base.js
index 990818837e05c..2725a5c466aea 100644
--- a/services/liberapay/liberapay-base.js
+++ b/services/liberapay/liberapay-base.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { colorScale } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { colorScale } from '../color-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
npatrons: nonNegativeInteger,
@@ -26,12 +24,11 @@ const schema = Joi.object({
.keys({
amount: Joi.string().required(),
})
- .allow(null)
- .required(),
+ .allow(null),
}).required()
const isCurrencyOverTime = Joi.string().regex(
- /^([0-9]*[1-9][0-9]*(\.[0-9]+)?|[0]+\.[0-9]*[1-9][0-9]*)[ A-Za-z]{4}\/week/
+ /^([0-9]*[1-9][0-9]*(\.[0-9]+)?|[0]+\.[0-9]*[1-9][0-9]*)[ A-Za-z]{4}\/week/,
)
function renderCurrencyBadge({ label, amount, currency }) {
@@ -43,16 +40,9 @@ function renderCurrencyBadge({ label, amount, currency }) {
}
class LiberapayBase extends BaseJsonService {
- static get category() {
- return 'funding'
- }
+ static category = 'funding'
- static get defaultBadgeData() {
- return {
- label: 'liberapay',
- namedLogo: 'liberapay',
- }
- }
+ static defaultBadgeData = { label: 'liberapay' }
async fetch({ entity }) {
return this._requestJson({
@@ -69,8 +59,4 @@ class LiberapayBase extends BaseJsonService {
}
}
-module.exports = {
- renderCurrencyBadge,
- LiberapayBase,
- isCurrencyOverTime,
-}
+export { renderCurrencyBadge, LiberapayBase, isCurrencyOverTime }
diff --git a/services/liberapay/liberapay-gives.service.js b/services/liberapay/liberapay-gives.service.js
index 6e41232aaca04..4e6491b26c6cc 100644
--- a/services/liberapay/liberapay-gives.service.js
+++ b/services/liberapay/liberapay-gives.service.js
@@ -1,25 +1,19 @@
-'use strict'
+import { InvalidResponse, pathParams } from '../index.js'
+import { renderCurrencyBadge, LiberapayBase } from './liberapay-base.js'
-const { InvalidResponse } = require('..')
-const { renderCurrencyBadge, LiberapayBase } = require('./liberapay-base')
+export default class LiberapayGives extends LiberapayBase {
+ static route = this.buildRoute('gives')
-module.exports = class LiberapayGives extends LiberapayBase {
- static get route() {
- return this.buildRoute('gives')
- }
-
- static get examples() {
- return [
- {
- title: 'Liberapay giving',
- namedParams: { entity: 'Changaco' },
- staticPreview: renderCurrencyBadge({
- label: 'gives',
- amount: '2.58',
- currency: 'EUR',
+ static openApi = {
+ '/liberapay/gives/{entity}': {
+ get: {
+ summary: 'Liberapay giving',
+ parameters: pathParams({
+ name: 'entity',
+ example: 'Changaco',
}),
},
- ]
+ },
}
async handle({ entity }) {
diff --git a/services/liberapay/liberapay-gives.tester.js b/services/liberapay/liberapay-gives.tester.js
index 1faec4be96dfc..1231fd632f88f 100644
--- a/services/liberapay/liberapay-gives.tester.js
+++ b/services/liberapay/liberapay-gives.tester.js
@@ -1,29 +1,37 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isCurrencyOverTime } from './liberapay-base.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { isCurrencyOverTime } = require('./liberapay-base')
-
-t.create('Giving (valid)')
- .get('/Changaco.json')
- .expectBadge({
- label: 'gives',
- message: isCurrencyOverTime,
- })
+t.create('Giving (valid)').get('/Changaco.json').expectBadge({
+ label: 'gives',
+ message: isCurrencyOverTime,
+})
t.create('Giving (not found)')
.get('/does-not-exist.json')
.expectBadge({ label: 'liberapay', message: 'not found' })
-t.create('Giving (null)')
+t.create('Giving (missing goal key)')
.get('/Liberapay.json')
.intercept(nock =>
nock('https://liberapay.com')
.get('/Liberapay/public.json')
.reply(200, {
npatrons: 0,
- giving: null,
+ giving: { amount: '3.71', currency: 'EUR' },
receiving: null,
- goal: null,
- })
+ }),
+ )
+ .expectBadge({ label: 'gives', message: isCurrencyOverTime })
+
+t.create('Giving (null)')
+ .get('/Liberapay.json')
+ .intercept(nock =>
+ nock('https://liberapay.com').get('/Liberapay/public.json').reply(200, {
+ npatrons: 0,
+ giving: null,
+ receiving: null,
+ goal: null,
+ }),
)
.expectBadge({ label: 'liberapay', message: 'no public giving stats' })
diff --git a/services/liberapay/liberapay-goal.service.js b/services/liberapay/liberapay-goal.service.js
index 47256a36c56e2..a1d9ce13aeeb2 100644
--- a/services/liberapay/liberapay-goal.service.js
+++ b/services/liberapay/liberapay-goal.service.js
@@ -1,22 +1,20 @@
-'use strict'
-
-const { colorScale } = require('../color-formatters')
-const { InvalidResponse } = require('..')
-const { LiberapayBase } = require('./liberapay-base')
-
-module.exports = class LiberapayGoal extends LiberapayBase {
- static get route() {
- return this.buildRoute('goal')
- }
-
- static get examples() {
- return [
- {
- title: 'Liberapay goal progress',
- namedParams: { entity: 'Changaco' },
- staticPreview: this.render({ percentAchieved: 33 }),
+import { colorScale } from '../color-formatters.js'
+import { InvalidResponse, pathParams } from '../index.js'
+import { LiberapayBase } from './liberapay-base.js'
+
+export default class LiberapayGoal extends LiberapayBase {
+ static route = this.buildRoute('goal')
+
+ static openApi = {
+ '/liberapay/goal/{entity}': {
+ get: {
+ summary: 'Liberapay goal progress',
+ parameters: pathParams({
+ name: 'entity',
+ example: 'Changaco',
+ }),
},
- ]
+ },
}
static render({ percentAchieved }) {
diff --git a/services/liberapay/liberapay-goal.spec.js b/services/liberapay/liberapay-goal.spec.js
index f0eee98de375e..d0f38677514ee 100644
--- a/services/liberapay/liberapay-goal.spec.js
+++ b/services/liberapay/liberapay-goal.spec.js
@@ -1,11 +1,9 @@
-'use strict'
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import LiberapayGoal from './liberapay-goal.service.js'
-const { expect } = require('chai')
-const { test, given } = require('sazerac')
-const { InvalidResponse } = require('..')
-const LiberapayGoal = require('./liberapay-goal.service')
-
-describe('LiberapayGoal', function() {
+describe('LiberapayGoal', function () {
test(LiberapayGoal.prototype.transform, () => {
given({ goal: {}, receiving: null }).expect({
percentAchieved: 0,
@@ -15,9 +13,9 @@ describe('LiberapayGoal', function() {
})
})
- it('throws InvalidResponse on missing goals', function() {
+ it('throws InvalidResponse on missing goals', function () {
expect(() =>
- LiberapayGoal.prototype.transform({ goal: null, receiving: null })
+ LiberapayGoal.prototype.transform({ goal: null, receiving: null }),
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'no public goals')
diff --git a/services/liberapay/liberapay-goal.tester.js b/services/liberapay/liberapay-goal.tester.js
index 9fe3441388379..e8ae6433b1dc1 100644
--- a/services/liberapay/liberapay-goal.tester.js
+++ b/services/liberapay/liberapay-goal.tester.js
@@ -1,14 +1,22 @@
-'use strict'
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('Goal Progress (valid)').get('/Liberapay.json').expectBadge({
+ label: 'goal progress',
+ message: isIntegerPercentage,
+})
-t.create('Goal Progress (valid)')
+t.create('Goal (missing goal key)')
.get('/Liberapay.json')
- .expectBadge({
- label: 'goal progress',
- message: isIntegerPercentage,
- })
+ .intercept(nock =>
+ nock('https://liberapay.com').get('/Liberapay/public.json').reply(200, {
+ npatrons: 0,
+ giving: null,
+ receiving: null,
+ }),
+ )
+ .expectBadge({ label: 'liberapay', message: 'no public goals' })
t.create('Goal Progress (not found)')
.get('/does-not-exist.json')
diff --git a/services/liberapay/liberapay-patrons.service.js b/services/liberapay/liberapay-patrons.service.js
index 1f8e7fc60d6f3..b21d265306069 100644
--- a/services/liberapay/liberapay-patrons.service.js
+++ b/services/liberapay/liberapay-patrons.service.js
@@ -1,22 +1,21 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { colorScale } from '../color-formatters.js'
+import { LiberapayBase } from './liberapay-base.js'
-const { metric } = require('../text-formatters')
-const { colorScale } = require('../color-formatters')
-const { LiberapayBase } = require('./liberapay-base')
+export default class LiberapayPatrons extends LiberapayBase {
+ static route = this.buildRoute('patrons')
-module.exports = class LiberapayPatrons extends LiberapayBase {
- static get route() {
- return this.buildRoute('patrons')
- }
-
- static get examples() {
- return [
- {
- title: 'Liberapay patrons',
- namedParams: { entity: 'Changaco' },
- staticPreview: this.render({ patrons: 10 }),
+ static openApi = {
+ '/liberapay/patrons/{entity}': {
+ get: {
+ summary: 'Liberapay patrons',
+ parameters: pathParams({
+ name: 'entity',
+ example: 'Changaco',
+ }),
},
- ]
+ },
}
static render({ patrons }) {
diff --git a/services/liberapay/liberapay-patrons.tester.js b/services/liberapay/liberapay-patrons.tester.js
index d6e78b788cc21..8c7f780c41cf5 100644
--- a/services/liberapay/liberapay-patrons.tester.js
+++ b/services/liberapay/liberapay-patrons.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Patrons (valid)')
- .get('/Liberapay.json')
- .expectBadge({
- label: 'patrons',
- message: isMetric,
- })
+t.create('Patrons (valid)').get('/Liberapay.json').expectBadge({
+ label: 'patrons',
+ message: isMetric,
+})
t.create('Patrons (not found)')
.get('/does-not-exist.json')
diff --git a/services/liberapay/liberapay-receives.service.js b/services/liberapay/liberapay-receives.service.js
index 3987a03c471ff..e44572c7f9b1f 100644
--- a/services/liberapay/liberapay-receives.service.js
+++ b/services/liberapay/liberapay-receives.service.js
@@ -1,25 +1,19 @@
-'use strict'
+import { InvalidResponse, pathParams } from '../index.js'
+import { renderCurrencyBadge, LiberapayBase } from './liberapay-base.js'
-const { InvalidResponse } = require('..')
-const { renderCurrencyBadge, LiberapayBase } = require('./liberapay-base')
+export default class LiberapayReceives extends LiberapayBase {
+ static route = this.buildRoute('receives')
-module.exports = class LiberapayReceives extends LiberapayBase {
- static get route() {
- return this.buildRoute('receives')
- }
-
- static get examples() {
- return [
- {
- title: 'Liberapay receiving',
- namedParams: { entity: 'Changaco' },
- staticPreview: renderCurrencyBadge({
- label: 'receives',
- amount: '98.32',
- currency: 'EUR',
+ static openApi = {
+ '/liberapay/receives/{entity}': {
+ get: {
+ summary: 'Liberapay receiving',
+ parameters: pathParams({
+ name: 'entity',
+ example: 'Changaco',
}),
},
- ]
+ },
}
async handle({ entity }) {
diff --git a/services/liberapay/liberapay-receives.tester.js b/services/liberapay/liberapay-receives.tester.js
index 3ae69dfff644e..765f57e6a1c3b 100644
--- a/services/liberapay/liberapay-receives.tester.js
+++ b/services/liberapay/liberapay-receives.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isCurrencyOverTime } from './liberapay-base.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { isCurrencyOverTime } = require('./liberapay-base')
-
-t.create('Receiving (valid)')
- .get('/Changaco.json')
- .expectBadge({
- label: 'receives',
- message: isCurrencyOverTime,
- })
+t.create('Receiving (valid)').get('/Changaco.json').expectBadge({
+ label: 'receives',
+ message: isCurrencyOverTime,
+})
t.create('Receiving (not found)')
.get('/does-not-exist.json')
@@ -17,13 +14,11 @@ t.create('Receiving (not found)')
t.create('Receiving (null)')
.get('/Liberapay.json')
.intercept(nock =>
- nock('https://liberapay.com')
- .get('/Liberapay/public.json')
- .reply(200, {
- npatrons: 0,
- giving: null,
- receiving: null,
- goal: null,
- })
+ nock('https://liberapay.com').get('/Liberapay/public.json').reply(200, {
+ npatrons: 0,
+ giving: null,
+ receiving: null,
+ goal: null,
+ }),
)
.expectBadge({ label: 'liberapay', message: 'no public receiving stats' })
diff --git a/services/librariesio/librariesio-api-provider.js b/services/librariesio/librariesio-api-provider.js
new file mode 100644
index 0000000000000..523d8b0e6b8c6
--- /dev/null
+++ b/services/librariesio/librariesio-api-provider.js
@@ -0,0 +1,111 @@
+import { ImproperlyConfigured } from '../index.js'
+import log from '../../core/server/log.js'
+import { TokenPool } from '../../core/token-pooling/token-pool.js'
+import { getUserAgent } from '../../core/base-service/got-config.js'
+
+const userAgent = getUserAgent()
+
+// Provides an interface to the Libraries.io API.
+export default class LibrariesIoApiProvider {
+ constructor({ baseUrl, tokens = [], defaultRateLimit = 60 }) {
+ const withPooling = tokens.length > 1
+ Object.assign(this, {
+ baseUrl,
+ withPooling,
+ globalToken: tokens[0],
+ defaultRateLimit,
+ })
+
+ if (this.withPooling) {
+ this.standardTokens = new TokenPool({ batchSize: 10 })
+ tokens.forEach(t => this.standardTokens.add(t, {}, defaultRateLimit))
+ }
+ }
+
+ getRateLimitFromHeaders({ headers, token }) {
+ // The Libraries.io API does not consistently provide the rate limiting headers.
+ // In some cases (e.g. package/version not founds) it won't include any of these headers,
+ // and the `retry-after` header is only provided _after_ the rate limit has been exceeded
+ // and requests are throttled.
+ //
+ // https://github.com/librariesio/libraries.io/issues/2860
+
+ // The standard rate limit is 60/requests/minute, so fallback to that default
+ // if the header isn't present.
+ // https://libraries.io/api#rate-limit
+ const rateLimit = +headers['x-ratelimit-limit'] || this.defaultRateLimit
+
+ // If the remaining header is missing, then we're in the 404 response phase, and simply
+ // subtract one from the `usesRemaining` count on the token, since the 404 responses do count
+ // against the rate limits.
+ const totalUsesRemaining =
+ +headers['x-ratelimit-remaining'] || token.decrementedUsesRemaining
+
+ // The `retry-after` header is only present post-rate limit excess, and contains the value in
+ // seconds the client needs to wait before the limits are reset.
+ // Our token pools internally use UTC-based milliseconds, so we perform the conversion
+ // if the header is present to ensure the token pool has the correct value.
+ // If the header is absent, we just use the current timestamp to
+ // advance the value to _something_
+ const retryAfter = headers['retry-after']
+ const nextReset =
+ ((Date.now() + (retryAfter ? +retryAfter * 1000 : 0)) / 1000) >>> 0
+
+ return {
+ rateLimit,
+ totalUsesRemaining,
+ nextReset,
+ }
+ }
+
+ updateToken({ token, res }) {
+ const { totalUsesRemaining, nextReset } = this.getRateLimitFromHeaders({
+ headers: res.headers,
+ token,
+ })
+ token.update(totalUsesRemaining, nextReset)
+ }
+
+ async fetch(requestFetcher, url, options = {}) {
+ const { baseUrl } = this
+
+ let token
+ let tokenString
+ if (this.withPooling) {
+ try {
+ token = this.standardTokens.next()
+ } catch (e) {
+ log.error(e)
+ throw new ImproperlyConfigured({
+ prettyMessage: 'Unable to select next Libraries.io token from pool',
+ })
+ }
+ tokenString = token.id
+ } else {
+ tokenString = this.globalToken
+ }
+
+ const mergedOptions = {
+ ...options,
+ ...{
+ headers: {
+ 'User-Agent': userAgent,
+ ...options.headers,
+ },
+ searchParams: {
+ api_key: tokenString,
+ ...options.searchParams,
+ },
+ },
+ }
+ const response = await requestFetcher(`${baseUrl}${url}`, mergedOptions)
+ if (this.withPooling) {
+ if (response.res.statusCode === 401) {
+ this.invalidateToken(token)
+ } else if (response.res.statusCode < 500) {
+ this.updateToken({ token, url, res: response.res })
+ }
+ }
+ return response
+ }
+}
diff --git a/services/librariesio/librariesio-api-provider.spec.js b/services/librariesio/librariesio-api-provider.spec.js
new file mode 100644
index 0000000000000..b2604d1c7f6eb
--- /dev/null
+++ b/services/librariesio/librariesio-api-provider.spec.js
@@ -0,0 +1,134 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { ImproperlyConfigured } from '../index.js'
+import log from '../../core/server/log.js'
+import LibrariesIoApiProvider from './librariesio-api-provider.js'
+
+describe('LibrariesIoApiProvider', function () {
+ const baseUrl = 'https://libraries.io/api'
+ const tokens = ['abc123', 'def456']
+ const rateLimit = 60
+ const remaining = 57
+ const nextReset = 60
+ const mockResponse = {
+ res: {
+ statusCode: 200,
+ headers: {
+ 'x-ratelimit-limit': `${rateLimit}`,
+ 'x-ratelimit-remaining': `${remaining}`,
+ 'retry-after': `${nextReset}`,
+ },
+ },
+ buffer: {},
+ }
+
+ let token, provider, nextTokenStub
+ beforeEach(function () {
+ provider = new LibrariesIoApiProvider({ baseUrl, tokens })
+
+ token = {
+ update: sinon.spy(),
+ invalidate: sinon.spy(),
+ decrementedUsesRemaining: remaining - 1,
+ }
+ nextTokenStub = sinon.stub(provider.standardTokens, 'next').returns(token)
+ })
+
+ afterEach(function () {
+ sinon.restore()
+ })
+
+ context('a core API request', function () {
+ const mockResponse = { res: { headers: {} } }
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ it('should obtain an appropriate token', async function () {
+ await provider.fetch(mockRequest, '/npm/badge-maker')
+ expect(provider.standardTokens.next).to.have.been.calledOnce
+ })
+
+ it('should throw an error when the next token fails', async function () {
+ nextTokenStub.throws(Error)
+ sinon.stub(log, 'error')
+ try {
+ await provider.fetch(mockRequest, '/npm/badge-maker')
+ expect.fail('Expected to throw')
+ } catch (e) {
+ expect(e).to.be.an.instanceof(ImproperlyConfigured)
+ expect(e.prettyMessage).to.equal(
+ 'Unable to select next Libraries.io token from pool',
+ )
+ }
+ })
+ })
+
+ context('a valid API response', function () {
+ const mockRequest = sinon.stub().resolves(mockResponse)
+ const tickTime = 123456789
+
+ beforeEach(function () {
+ const clock = sinon.useFakeTimers()
+ clock.tick(tickTime)
+ })
+
+ it('should return the response', async function () {
+ const res = await provider.fetch(mockRequest, '/npm/badge-maker')
+ expect(Object.is(res, mockResponse)).to.be.true
+ })
+
+ it('should update the token with the expected values when headers are present', async function () {
+ await provider.fetch(mockRequest, '/npm/badge-maker')
+
+ expect(token.update).to.have.been.calledWith(
+ remaining,
+ ((nextReset * 1000 + tickTime) / 1000) >>> 0,
+ )
+ expect(token.invalidate).not.to.have.been.called
+ })
+
+ it('should update the token with the expected values when throttling not applied', async function () {
+ const response = {
+ res: {
+ statusCode: 200,
+ headers: {
+ 'x-ratelimit-limit': rateLimit,
+ 'x-ratelimit-remaining': remaining,
+ },
+ },
+ }
+ const mockRequest = sinon.stub().resolves(response)
+ await provider.fetch(mockRequest, '/npm/badge-maker')
+
+ expect(token.update).to.have.been.calledWith(
+ remaining,
+ (tickTime / 1000) >>> 0,
+ )
+ expect(token.invalidate).not.to.have.been.called
+ })
+
+ it('should update the token with the expected values in 404 case', async function () {
+ const response = {
+ res: { statusCode: 200, headers: {} },
+ }
+ const mockRequest = sinon.stub().resolves(response)
+ await provider.fetch(mockRequest, '/npm/badge-maker')
+
+ expect(token.update).to.have.been.calledWith(
+ remaining - 1,
+ (tickTime / 1000) >>> 0,
+ )
+ expect(token.invalidate).not.to.have.been.called
+ })
+ })
+
+ context('a connection error', function () {
+ const msg = 'connection timeout'
+ const requestError = new Error(msg)
+ const mockRequest = sinon.stub().rejects(requestError)
+
+ it('should throw an exception', async function () {
+ return expect(
+ provider.fetch(mockRequest, '/npm/badge-maker', {}),
+ ).to.be.rejectedWith(Error, 'connection timeout')
+ })
+ })
+})
diff --git a/services/librariesio/librariesio-base.js b/services/librariesio/librariesio-base.js
new file mode 100644
index 0000000000000..8d221c0a35051
--- /dev/null
+++ b/services/librariesio/librariesio-base.js
@@ -0,0 +1,32 @@
+import Joi from 'joi'
+import { anyInteger, nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
+
+// API doc: https://libraries.io/api#project
+const projectSchema = Joi.object({
+ platform: Joi.string().required(),
+ dependents_count: nonNegativeInteger,
+ dependent_repos_count: nonNegativeInteger,
+ rank: anyInteger,
+}).required()
+
+export default class LibrariesIoBase extends BaseJsonService {
+ constructor(context, config) {
+ super(context, config)
+ const { requestFetcher, librariesIoApiProvider } = context
+ this._requestFetcher = librariesIoApiProvider.fetch.bind(
+ librariesIoApiProvider,
+ requestFetcher,
+ )
+ }
+
+ async fetchProject({ platform, scope, packageName }) {
+ return this._requestJson({
+ schema: projectSchema,
+ url: `/${encodeURIComponent(platform)}/${
+ scope ? encodeURIComponent(`${scope}/`) : ''
+ }${encodeURIComponent(packageName)}`,
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+}
diff --git a/services/librariesio/librariesio-common.js b/services/librariesio/librariesio-common.js
deleted file mode 100644
index 59b56e346c40f..0000000000000
--- a/services/librariesio/librariesio-common.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger, anyInteger } = require('../validators')
-
-// API doc: https://libraries.io/api#project
-const projectSchema = Joi.object({
- platform: Joi.string().required(),
- dependents_count: nonNegativeInteger,
- dependent_repos_count: nonNegativeInteger,
- rank: anyInteger,
-}).required()
-
-async function fetchProject(serviceInstance, { platform, scope, packageName }) {
- return serviceInstance._requestJson({
- schema: projectSchema,
- url: `https://libraries.io/api/${encodeURIComponent(platform)}/${
- scope ? encodeURIComponent(`${scope}/`) : ''
- }${encodeURIComponent(packageName)}`,
- errorMessages: { 404: 'package not found' },
- })
-}
-
-module.exports = {
- fetchProject,
-}
diff --git a/services/librariesio/librariesio-constellation.js b/services/librariesio/librariesio-constellation.js
new file mode 100644
index 0000000000000..0f991f54540ff
--- /dev/null
+++ b/services/librariesio/librariesio-constellation.js
@@ -0,0 +1,13 @@
+import LibrariesIoApiProvider from './librariesio-api-provider.js'
+
+// Convenience class with all the stuff related to the Libraries.io API and its
+// authorization tokens, to simplify server initialization.
+export default class LibrariesIoConstellation {
+ constructor({ private: { librariesio_tokens: tokens } }) {
+ this.apiProvider = new LibrariesIoApiProvider({
+ baseUrl: 'https://libraries.io/api',
+ tokens,
+ defaultRateLimit: 60,
+ })
+ }
+}
diff --git a/services/librariesio/librariesio-dependencies-helpers.js b/services/librariesio/librariesio-dependencies-helpers.js
index f9bf0b1908ca5..7aaaf71536a37 100644
--- a/services/librariesio/librariesio-dependencies-helpers.js
+++ b/services/librariesio/librariesio-dependencies-helpers.js
@@ -1,5 +1,3 @@
-'use strict'
-
function transform({ dependencies }) {
return {
deprecatedCount: dependencies.filter(dep => dep.deprecated).length,
@@ -28,4 +26,4 @@ function renderDependenciesBadge({ deprecatedCount, outdatedCount }) {
}
}
-module.exports = { transform, renderDependenciesBadge }
+export { transform, renderDependenciesBadge }
diff --git a/services/librariesio/librariesio-dependencies-helpers.spec.js b/services/librariesio/librariesio-dependencies-helpers.spec.js
index d3c021a1fc630..bdc1c3d17a15e 100644
--- a/services/librariesio/librariesio-dependencies-helpers.spec.js
+++ b/services/librariesio/librariesio-dependencies-helpers.spec.js
@@ -1,11 +1,7 @@
-'use strict'
+import { test, given, forCases } from 'sazerac'
+import { renderDependenciesBadge } from './librariesio-dependencies-helpers.js'
-const { test, given, forCases } = require('sazerac')
-const {
- renderDependenciesBadge,
-} = require('./librariesio-dependencies-helpers')
-
-describe('Libraries.io dependency helpers', function() {
+describe('Libraries.io dependency helpers', function () {
test(renderDependenciesBadge, () => {
forCases([
given({ deprecatedCount: 10, outdatedCount: 0 }),
diff --git a/services/librariesio/librariesio-dependencies.service.js b/services/librariesio/librariesio-dependencies.service.js
index 009de28404d4e..7be34ff51ad23 100644
--- a/services/librariesio/librariesio-dependencies.service.js
+++ b/services/librariesio/librariesio-dependencies.service.js
@@ -1,153 +1,113 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-const {
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { pathParams } from '../index.js'
+import LibrariesIoBase from './librariesio-base.js'
+import {
transform,
renderDependenciesBadge,
-} = require('./librariesio-dependencies-helpers')
+} from './librariesio-dependencies-helpers.js'
-const schema = Joi.object({
+const projectDependenciesSchema = Joi.object({
dependencies: Joi.array()
.items(
Joi.object({
- deprecated: Joi.boolean()
- .allow(null)
- .required(),
- outdated: Joi.boolean()
- .allow(null)
- .required(),
- })
+ deprecated: Joi.boolean().allow(null).required(),
+ outdated: Joi.boolean().allow(null).required(),
+ }),
)
.default([]),
}).required()
-class LibrariesIoProjectDependencies extends BaseJsonService {
- static get category() {
- return 'dependencies'
- }
+const repoDependenciesSchema = Joi.object({
+ deprecated_count: nonNegativeInteger,
+ outdated_count: nonNegativeInteger,
+}).required()
+
+class LibrariesIoProjectDependencies extends LibrariesIoBase {
+ static category = 'dependencies'
- static get route() {
- return {
- base: 'librariesio/release',
- pattern: ':platform/:scope(@[^/]+)?/:packageName/:version?',
- }
+ static route = {
+ base: 'librariesio/release',
+ pattern: ':platform/:scope(@[^/]+)?/:packageName/:version?',
}
- static get examples() {
- return [
- {
- title: 'Libraries.io dependency status for latest release',
- pattern: ':platform/:packageName',
- namedParams: {
- platform: 'hex',
- packageName: 'phoenix',
- },
- staticPreview: renderDependenciesBadge({
- deprecatedCount: 0,
- outdatedCount: 1,
- }),
+ static openApi = {
+ '/librariesio/release/{platform}/{packageName}': {
+ get: {
+ summary: 'Libraries.io dependency status for latest release',
+ parameters: pathParams(
+ { name: 'platform', example: 'npm' },
+ { name: 'packageName', example: '@babel/core' },
+ ),
},
- {
- title: 'Libraries.io dependency status for specific release',
- pattern: ':platform/:packageName/:version',
- namedParams: {
- platform: 'hex',
- packageName: 'phoenix',
- version: '1.0.3',
- },
- staticPreview: renderDependenciesBadge({
- deprecatedCount: 0,
- outdatedCount: 3,
- }),
- },
- {
- title:
- 'Libraries.io dependency status for latest release, scoped npm package',
- pattern: ':platform/:scope/:packageName',
- namedParams: {
- platform: 'npm',
- scope: '@babel',
- packageName: 'core',
- },
- staticPreview: renderDependenciesBadge({
- deprecatedCount: 8,
- outdatedCount: 0,
- }),
+ },
+ '/librariesio/release/{platform}/{packageName}/{version}': {
+ get: {
+ summary: 'Libraries.io dependency status for specific release',
+ parameters: pathParams(
+ { name: 'platform', example: 'npm' },
+ { name: 'packageName', example: '@babel/core' },
+ { name: 'version', example: '7.0.0' },
+ ),
},
- {
- title:
- 'Libraries.io dependency status for specific release, scoped npm package',
- pattern: ':platform/:scope/:packageName/:version',
- namedParams: {
- platform: 'npm',
- scope: '@babel',
- packageName: 'core',
- version: '7.0.0',
- },
- staticPreview: renderDependenciesBadge({
- deprecatedCount: 12,
- outdatedCount: 0,
- }),
- },
- ]
+ },
}
+ static _cacheLength = 900
+
async handle({ platform, scope, packageName, version = 'latest' }) {
- const url = `https://libraries.io/api/${encodeURIComponent(platform)}/${
+ const url = `/${encodeURIComponent(platform)}/${
scope ? encodeURIComponent(`${scope}/`) : ''
}${encodeURIComponent(packageName)}/${encodeURIComponent(
- version
+ version,
)}/dependencies`
const json = await this._requestJson({
url,
- schema,
- errorMessages: { 404: 'package or version not found' },
+ schema: projectDependenciesSchema,
+ httpErrors: { 404: 'package or version not found' },
})
const { deprecatedCount, outdatedCount } = transform(json)
return renderDependenciesBadge({ deprecatedCount, outdatedCount })
}
}
-class LibrariesIoRepoDependencies extends BaseJsonService {
- static get category() {
- return 'dependencies'
- }
+class LibrariesIoRepoDependencies extends LibrariesIoBase {
+ static category = 'dependencies'
- static get route() {
- return {
- base: 'librariesio/github',
- pattern: ':user/:repo',
- }
+ static route = {
+ base: 'librariesio/github',
+ pattern: ':user/:repo',
}
- static get examples() {
- return [
- {
- title: 'Libraries.io dependency status for GitHub repo',
- namedParams: {
- user: 'phoenixframework',
- repo: 'phoenix',
- },
- staticPreview: renderDependenciesBadge({ outdatedCount: 325 }),
+ static openApi = {
+ '/librariesio/github/{user}/{repo}': {
+ get: {
+ summary: 'Libraries.io dependency status for GitHub repo',
+ parameters: pathParams(
+ { name: 'user', example: 'phoenixframework' },
+ { name: 'repo', example: 'phoenix' },
+ ),
},
- ]
+ },
}
+ static _cacheLength = 900
+
async handle({ user, repo }) {
- const url = `https://libraries.io/api/github/${encodeURIComponent(
- user
- )}/${encodeURIComponent(repo)}/dependencies`
- const json = await this._requestJson({
- url,
- schema,
- errorMessages: {
- 404: 'repo not found',
- },
- })
- const { deprecatedCount, outdatedCount } = transform(json)
+ const url = `/github/${encodeURIComponent(user)}/${encodeURIComponent(
+ repo,
+ )}/shields_dependencies`
+
+ const { deprecated_count: deprecatedCount, outdated_count: outdatedCount } =
+ await this._requestJson({
+ url,
+ schema: repoDependenciesSchema,
+ httpErrors: {
+ 404: 'repo not found',
+ },
+ })
return renderDependenciesBadge({ deprecatedCount, outdatedCount })
}
}
-module.exports = [LibrariesIoProjectDependencies, LibrariesIoRepoDependencies]
+export default [LibrariesIoProjectDependencies, LibrariesIoRepoDependencies]
diff --git a/services/librariesio/librariesio-dependencies.tester.js b/services/librariesio/librariesio-dependencies.tester.js
index 45bcabddbe6d7..6aeb2feda475a 100644
--- a/services/librariesio/librariesio-dependencies.tester.js
+++ b/services/librariesio/librariesio-dependencies.tester.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { isDependencyState } = require('../test-validators')
-const t = (module.exports = new ServiceTester({
+import { ServiceTester } from '../tester.js'
+import { isDependencyState } from '../test-validators.js'
+export const t = new ServiceTester({
id: 'LibrariesIoDependencies',
title: 'LibrariesIoDependencies',
pathPrefix: '/librariesio',
-}))
+})
t.create('dependencies for package (project name contains dot)')
.timeout(10000)
diff --git a/services/librariesio/librariesio-dependent-repos.service.js b/services/librariesio/librariesio-dependent-repos.service.js
index 00bacd947904c..e8b4c1765983b 100644
--- a/services/librariesio/librariesio-dependent-repos.service.js
+++ b/services/librariesio/librariesio-dependent-repos.service.js
@@ -1,50 +1,57 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
-const { fetchProject } = require('./librariesio-common')
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import LibrariesIoBase from './librariesio-base.js'
// https://libraries.io/api#project-dependent-repositories
-module.exports = class LibrariesIoDependentRepos extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class LibrariesIoDependentRepos extends LibrariesIoBase {
+ static category = 'other'
- static get route() {
- return {
- base: 'librariesio/dependent-repos',
- pattern: ':platform/:scope(@[^/]+)?/:packageName',
- }
+ static route = {
+ base: 'librariesio/dependent-repos',
+ pattern: ':platform/:scope(@[^/]+)?/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Dependent repos (via libraries.io)',
- pattern: ':platform/:packageName',
- namedParams: {
- platform: 'npm',
- packageName: 'got',
- },
- staticPreview: this.render({ dependentReposCount: 84000 }),
+ static openApi = {
+ '/librariesio/dependent-repos/{platform}/{packageName}': {
+ get: {
+ summary: 'Dependent repos (via libraries.io)',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'packageName',
+ example: 'got',
+ },
+ ),
},
- {
- title: 'Dependent repos (via libraries.io), scoped npm package',
- pattern: ':platform/:scope/:packageName',
- namedParams: {
- platform: 'npm',
- scope: '@babel',
- packageName: 'core',
- },
- staticPreview: this.render({ dependentReposCount: 50 }),
+ },
+ '/librariesio/dependent-repos/{platform}/{scope}/{packageName}': {
+ get: {
+ summary: 'Dependent repos (via libraries.io), scoped npm package',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'scope',
+ example: '@babel',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'dependent repos',
- }
+ static _cacheLength = 900
+
+ static defaultBadgeData = {
+ label: 'dependent repos',
}
static render({ dependentReposCount }) {
@@ -55,14 +62,12 @@ module.exports = class LibrariesIoDependentRepos extends BaseJsonService {
}
async handle({ platform, scope, packageName }) {
- const { dependent_repos_count: dependentReposCount } = await fetchProject(
- this,
- {
+ const { dependent_repos_count: dependentReposCount } =
+ await this.fetchProject({
platform,
scope,
packageName,
- }
- )
+ })
return this.constructor.render({ dependentReposCount })
}
}
diff --git a/services/librariesio/librariesio-dependent-repos.tester.js b/services/librariesio/librariesio-dependent-repos.tester.js
index f15fe079cda71..2c7df20c70801 100644
--- a/services/librariesio/librariesio-dependent-repos.tester.js
+++ b/services/librariesio/librariesio-dependent-repos.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('dependent repo count')
.timeout(10000)
diff --git a/services/librariesio/librariesio-dependents.service.js b/services/librariesio/librariesio-dependents.service.js
index 4bb7346c21622..0dfaed871d1f2 100644
--- a/services/librariesio/librariesio-dependents.service.js
+++ b/services/librariesio/librariesio-dependents.service.js
@@ -1,50 +1,57 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { BaseJsonService } = require('..')
-const { fetchProject } = require('./librariesio-common')
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import LibrariesIoBase from './librariesio-base.js'
// https://libraries.io/api#project-dependents
-module.exports = class LibrariesIoDependents extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class LibrariesIoDependents extends LibrariesIoBase {
+ static category = 'other'
- static get route() {
- return {
- base: 'librariesio/dependents',
- pattern: ':platform/:scope(@[^/]+)?/:packageName',
- }
+ static route = {
+ base: 'librariesio/dependents',
+ pattern: ':platform/:scope(@[^/]+)?/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Dependents (via libraries.io)',
- pattern: ':platform/:packageName',
- namedParams: {
- platform: 'npm',
- packageName: 'got',
- },
- staticPreview: this.render({ dependentCount: 2000 }),
+ static openApi = {
+ '/librariesio/dependents/{platform}/{packageName}': {
+ get: {
+ summary: 'Dependents (via libraries.io)',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'packageName',
+ example: 'got',
+ },
+ ),
},
- {
- title: 'Dependents (via libraries.io), scoped npm package',
- pattern: ':platform/:scope/:packageName',
- namedParams: {
- platform: 'npm',
- scope: '@babel',
- packageName: 'core',
- },
- staticPreview: this.render({ dependentCount: 94 }),
+ },
+ '/librariesio/dependents/{platform}/{scope}/{packageName}': {
+ get: {
+ summary: 'Dependents (via libraries.io), scoped npm package',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'scope',
+ example: '@babel',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'dependents',
- }
+ static _cacheLength = 900
+
+ static defaultBadgeData = {
+ label: 'dependents',
}
static render({ dependentCount }) {
@@ -55,7 +62,7 @@ module.exports = class LibrariesIoDependents extends BaseJsonService {
}
async handle({ platform, scope, packageName }) {
- const { dependents_count: dependentCount } = await fetchProject(this, {
+ const { dependents_count: dependentCount } = await this.fetchProject({
platform,
scope,
packageName,
diff --git a/services/librariesio/librariesio-dependents.tester.js b/services/librariesio/librariesio-dependents.tester.js
index e5cfcd29c2863..8ca0b4393bd31 100644
--- a/services/librariesio/librariesio-dependents.tester.js
+++ b/services/librariesio/librariesio-dependents.tester.js
@@ -1,15 +1,11 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('dependent count')
- .timeout(10000)
- .get('/npm/got.json')
- .expectBadge({
- label: 'dependents',
- message: isMetric,
- })
+t.create('dependent count').timeout(10000).get('/npm/got.json').expectBadge({
+ label: 'dependents',
+ message: isMetric,
+})
t.create('dependent count (scoped npm package)')
.timeout(10000)
diff --git a/services/librariesio/librariesio-sourcerank.service.js b/services/librariesio/librariesio-sourcerank.service.js
index 5d0f5bc8d622b..dacc98b75e6e1 100644
--- a/services/librariesio/librariesio-sourcerank.service.js
+++ b/services/librariesio/librariesio-sourcerank.service.js
@@ -1,51 +1,56 @@
-'use strict'
-
-const { colorScale } = require('../color-formatters')
-const { BaseJsonService } = require('..')
-const { fetchProject } = require('./librariesio-common')
+import { pathParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import LibrariesIoBase from './librariesio-base.js'
const sourceRankColor = colorScale([10, 15, 20, 25, 30])
-module.exports = class LibrariesIoSourcerank extends BaseJsonService {
- static get category() {
- return 'rating'
- }
+export default class LibrariesIoSourcerank extends LibrariesIoBase {
+ static category = 'rating'
- static get route() {
- return {
- base: 'librariesio/sourcerank',
- pattern: ':platform/:scope(@[^/]+)?/:packageName',
- }
+ static route = {
+ base: 'librariesio/sourcerank',
+ pattern: ':platform/:scope(@[^/]+)?/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Libraries.io SourceRank',
- pattern: ':platform/:packageName',
- namedParams: {
- platform: 'npm',
- packageName: 'got',
- },
- staticPreview: this.render({ rank: 25 }),
+ static openApi = {
+ '/librariesio/sourcerank/{platform}/{packageName}': {
+ get: {
+ summary: 'Libraries.io SourceRank',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'packageName',
+ example: 'got',
+ },
+ ),
},
- {
- title: 'Libraries.io SourceRank, scoped npm package',
- pattern: ':platform/:scope/:packageName',
- namedParams: {
- platform: 'npm',
- scope: '@babel',
- packageName: 'core',
- },
- staticPreview: this.render({ rank: 3 }),
+ },
+ '/librariesio/sourcerank/{platform}/{scope}/{packageName}': {
+ get: {
+ summary: 'Libraries.io SourceRank, scoped npm package',
+ parameters: pathParams(
+ {
+ name: 'platform',
+ example: 'npm',
+ },
+ {
+ name: 'scope',
+ example: '@babel',
+ },
+ {
+ name: 'packageName',
+ example: 'core',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'sourcerank',
- }
+ static defaultBadgeData = {
+ label: 'sourcerank',
}
static render({ rank }) {
@@ -56,7 +61,7 @@ module.exports = class LibrariesIoSourcerank extends BaseJsonService {
}
async handle({ platform, scope, packageName }) {
- const { rank } = await fetchProject(this, {
+ const { rank } = await this.fetchProject({
platform,
scope,
packageName,
diff --git a/services/librariesio/librariesio-sourcerank.spec.js b/services/librariesio/librariesio-sourcerank.spec.js
new file mode 100644
index 0000000000000..6e48f675dce84
--- /dev/null
+++ b/services/librariesio/librariesio-sourcerank.spec.js
@@ -0,0 +1,52 @@
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import LibrariesIoSourcerank from './librariesio-sourcerank.service.js'
+import LibrariesIoApiProvider from './librariesio-api-provider.js'
+
+describe('LibrariesIoSourcerank', function () {
+ cleanUpNockAfterEach()
+ const fakeApiKey = 'fakeness'
+ const response = {
+ platform: 'npm',
+ dependents_count: 150,
+ dependent_repos_count: 191,
+ rank: 100,
+ }
+ const config = {
+ private: {
+ librariesio_tokens: fakeApiKey,
+ },
+ }
+ const librariesIoApiProvider = new LibrariesIoApiProvider({
+ baseUrl: 'https://libraries.io/api',
+ tokens: [fakeApiKey],
+ })
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://libraries.io/api')
+ // This ensures that the expected credentials are actually being sent with the HTTP request.
+ // Without this the request wouldn't match and the test would fail.
+ .get(`/npm/badge-maker?api_key=${fakeApiKey}`)
+ .reply(200, response)
+
+ expect(
+ await LibrariesIoSourcerank.invoke(
+ {
+ ...defaultContext,
+ librariesIoApiProvider,
+ },
+ config,
+ {
+ platform: 'npm',
+ packageName: 'badge-maker',
+ },
+ ),
+ ).to.deep.equal({
+ message: 100,
+ color: 'brightgreen',
+ })
+
+ scope.done()
+ })
+})
diff --git a/services/librariesio/librariesio-sourcerank.tester.js b/services/librariesio/librariesio-sourcerank.tester.js
index 2a8450fa82e37..7135302de37c1 100644
--- a/services/librariesio/librariesio-sourcerank.tester.js
+++ b/services/librariesio/librariesio-sourcerank.tester.js
@@ -1,15 +1,11 @@
-'use strict'
+import { anyInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { anyInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('sourcerank')
- .timeout(10000)
- .get('/npm/got.json')
- .expectBadge({
- label: 'sourcerank',
- message: anyInteger,
- })
+t.create('sourcerank').timeout(10000).get('/npm/got.json').expectBadge({
+ label: 'sourcerank',
+ message: anyInteger,
+})
t.create('sourcerank (scoped npm package)')
.timeout(10000)
diff --git a/services/libscore/libscore.service.js b/services/libscore/libscore.service.js
deleted file mode 100644
index 6dd0a755a9aae..0000000000000
--- a/services/libscore/libscore.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'rating',
- route: {
- base: 'libscore/s',
- pattern: ':various+',
- },
- label: 'libscore',
- dateAdded: new Date('2018-09-22'),
-})
diff --git a/services/libscore/libscore.tester.js b/services/libscore/libscore.tester.js
deleted file mode 100644
index 223e8b3d58962..0000000000000
--- a/services/libscore/libscore.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({ id: 'libscore', title: 'libscore' })
-module.exports = t
-
-t.create('no longer available (previously usage statistics)')
- .get('/s/jQuery.json')
- .expectBadge({
- label: 'libscore',
- message: 'no longer available',
- })
diff --git a/services/licenses.js b/services/licenses.js
index b6efcb8ae0d15..bef445cc6865e 100644
--- a/services/licenses.js
+++ b/services/licenses.js
@@ -1,6 +1,10 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to license badges.
+ *
+ * @module
+ */
-const toArray = require('../core/base-service/to-array')
+import toArray from '../core/base-service/to-array.js'
const licenseTypes = {
// permissive licenses - not public domain and not copyleft
@@ -83,13 +87,18 @@ const licenseTypes = {
},
// public domain licenses do not require 'License and copyright notice' (https://choosealicense.com/appendix/#include-copyright)
'public-domain': {
- spdxLicenseIds: ['CC0-1.0', 'Unlicense', 'WTFPL'],
+ spdxLicenseIds: ['CC0-1.0', 'Unlicense', 'WTFPL', '0BSD'],
aliases: ['CC0'],
color: '7cd958',
priority: '3',
},
}
+/**
+ * Mapping of licenses to their corresponding color and priority.
+ *
+ * @type {object}
+ */
const licenseToColorMap = {}
Object.keys(licenseTypes).forEach(licenseType => {
const { spdxLicenseIds, aliases, color, priority } = licenseTypes[licenseType]
@@ -101,6 +110,12 @@ Object.keys(licenseTypes).forEach(licenseType => {
})
})
+/**
+ * Maps the license to its corresponding color and priority and sorts the list of mapped licenses by priority.
+ *
+ * @param {string | string[]} licenses License or list of licenses
+ * @returns {string} Color corresponding to the license or the list of licenses
+ */
function licenseToColor(licenses) {
if (!Array.isArray(licenses)) {
licenses = [licenses]
@@ -115,7 +130,18 @@ function licenseToColor(licenses) {
return color
}
-function renderLicenseBadge({ license, licenses }) {
+/**
+ * Handles rendering concerns of license badges.
+ * Determines the message of the badge by joining the licenses in a comma-separated format.
+ * Sets the badge color to the provided value, if not provided then the color is used from licenseToColorMap.
+ *
+ * @param {object} attrs Refer to individual attributes
+ * @param {string} [attrs.license] License to render, required if badge contains only one license
+ * @param {string[]} [attrs.licenses] List of licenses to render, required if badge contains multiple licenses
+ * @param {string} [attrs.color] If provided then the badge will use this color value
+ * @returns {object} Badge with message and color properties
+ */
+function renderLicenseBadge({ license, licenses, color }) {
if (licenses === undefined) {
licenses = toArray(license)
}
@@ -126,8 +152,8 @@ function renderLicenseBadge({ license, licenses }) {
return {
message: licenses.join(', '),
- color: licenseToColor(licenses),
+ color: color || licenseToColor(licenses),
}
}
-module.exports = { licenseToColor, renderLicenseBadge }
+export { licenseToColor, renderLicenseBadge }
diff --git a/services/licenses.spec.js b/services/licenses.spec.js
index 233d0485a7156..a82d10f5be8d7 100644
--- a/services/licenses.spec.js
+++ b/services/licenses.spec.js
@@ -1,13 +1,11 @@
-'use strict'
+import { test, given, forCases } from 'sazerac'
+import { licenseToColor, renderLicenseBadge } from './licenses.js'
-const { test, given, forCases } = require('sazerac')
-const { licenseToColor, renderLicenseBadge } = require('./licenses')
-
-describe('license helpers', function() {
+describe('license helpers', function () {
test(licenseToColor, () => {
forCases([given('MIT'), given('BSD')]).expect('green')
forCases([given('MPL-2.0'), given('MPL')]).expect('orange')
- forCases([given('Unlicense'), given('CC0')]).expect('7cd958')
+ forCases([given('Unlicense'), given('CC0'), given('0BSD')]).expect('7cd958')
forCases([given('unknown-license'), given(null)]).expect('lightgrey')
given(['CC0-1.0', 'MPL-2.0']).expect('7cd958')
@@ -42,5 +40,9 @@ describe('license helpers', function() {
message: 'MPL-2.0, MIT',
color: 'green',
})
+ given({ license: 'MIT', color: 'pink' }).expect({
+ message: 'MIT',
+ color: 'pink',
+ })
})
})
diff --git a/services/localizely/localizely.service.js b/services/localizely/localizely.service.js
new file mode 100644
index 0000000000000..b5e62b5fb564d
--- /dev/null
+++ b/services/localizely/localizely.service.js
@@ -0,0 +1,154 @@
+import Joi from 'joi'
+import {
+ BaseJsonService,
+ InvalidResponse,
+ queryParam,
+ pathParam,
+} from '../index.js'
+import { coveragePercentage } from '../color-formatters.js'
+
+const description = `
+Localizely is a management system for translation, localization, and internationalization of your projects.
+
+The read-only API token from the Localizely account is required to fetch necessary data.
+
+
+ Note: Do not use the default API token as it grants full read-write permissions to your projects. You will expose your project and allow malicious users to modify the translations at will.
+ Instead, create a new one with only read permission.
+
+
+You can find more details regarding API tokens under My profile page.
+`
+
+const schema = Joi.object({
+ strings: Joi.number().required(),
+ reviewedProgress: Joi.number().required(),
+ languages: Joi.array()
+ .items(
+ Joi.object({
+ langCode: Joi.string().required(),
+ langName: Joi.string().required(),
+ strings: Joi.number().required(),
+ reviewed: Joi.number().required(),
+ reviewedProgress: Joi.number().required(),
+ }),
+ )
+ .required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ token: Joi.string().required(),
+ languageCode: Joi.string().regex(/^[a-z]{2}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/),
+}).required()
+
+export default class Localizely extends BaseJsonService {
+ static category = 'other'
+
+ static route = {
+ base: 'localizely/progress',
+ pattern: ':projectId/:branch*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/localizely/progress/{projectId}': {
+ get: {
+ summary: 'Localizely progress',
+ description,
+ parameters: [
+ pathParam({
+ name: 'projectId',
+ example: '5cc34208-0418-40b1-8353-acc70c95f802',
+ }),
+ queryParam({
+ name: 'token',
+ example:
+ '0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ required: true,
+ }),
+ queryParam({
+ name: 'languageCode',
+ example: 'en-US',
+ required: false,
+ }),
+ ],
+ },
+ },
+ '/localizely/progress/{projectId}/{branch}': {
+ get: {
+ summary: 'Localizely progress (branch)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'projectId',
+ example: '5cc34208-0418-40b1-8353-acc70c95f802',
+ }),
+ pathParam({
+ name: 'branch',
+ example: 'main',
+ }),
+ queryParam({
+ name: 'token',
+ example:
+ '0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ required: true,
+ }),
+ queryParam({
+ name: 'languageCode',
+ example: 'en-US',
+ required: false,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'localized' }
+
+ static render({ langName, reviewedProgress }) {
+ return {
+ label: langName || 'localized',
+ message: `${reviewedProgress}%`,
+ color: coveragePercentage(reviewedProgress),
+ }
+ }
+
+ async fetch({ projectId, branch, apiToken }) {
+ return this._requestJson({
+ schema,
+ url: `https://api.localizely.com/v1/projects/${projectId}/status`,
+ options: {
+ searchParams: { branch },
+ headers: { 'X-Api-Token': apiToken },
+ },
+ httpErrors: {
+ 403: 'not authorized for project',
+ },
+ })
+ }
+
+ static transform(json, languageCode) {
+ if (!languageCode) {
+ return { reviewedProgress: json.reviewedProgress }
+ }
+
+ const language = json.languages.find(lang => lang.langCode === languageCode)
+ if (!language) {
+ throw new InvalidResponse({ prettyMessage: 'Unsupported language' })
+ }
+
+ const { langName, reviewedProgress } = language
+
+ return { langName, reviewedProgress }
+ }
+
+ async handle({ projectId, branch }, { token: apiToken, languageCode }) {
+ const json = await this.fetch({ projectId, branch, apiToken })
+ const { langName, reviewedProgress } = this.constructor.transform(
+ json,
+ languageCode,
+ )
+
+ return this.constructor.render({ langName, reviewedProgress })
+ }
+}
diff --git a/services/localizely/localizely.tester.js b/services/localizely/localizely.tester.js
new file mode 100644
index 0000000000000..39eb9f0fbf112
--- /dev/null
+++ b/services/localizely/localizely.tester.js
@@ -0,0 +1,132 @@
+import { createServiceTester } from '../tester.js'
+import { isIntegerPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Overall progress')
+ .get(
+ '/5cc34208-0418-40b1-8353-acc70c95f802.json?token=0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ )
+ .expectBadge({ label: 'localized', message: isIntegerPercentage })
+
+t.create('Overall progress on specific branch')
+ .get(
+ '/5cc34208-0418-40b1-8353-acc70c95f802/Version_1.0.json?token=0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ )
+ .expectBadge({ label: 'localized', message: isIntegerPercentage })
+
+t.create('Overall progress with invalid token')
+ .get(
+ '/1349592f-8d05-4317-9f46-bddc5def11fe/main.json?token=312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ )
+ .intercept(nock =>
+ nock('https://api.localizely.com', {
+ reqheaders: {
+ 'x-api-token':
+ '312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ },
+ })
+ .get('/v1/projects/1349592f-8d05-4317-9f46-bddc5def11fe/status')
+ .query({ branch: 'main' })
+ .reply(403, {
+ errorCode: 'forbidden',
+ errorMessage: 'Tried to access unauthorized project',
+ }),
+ )
+ .expectBadge({ label: 'localized', message: 'not authorized for project' })
+
+t.create('Language progress')
+ .get(
+ '/5cc34208-0418-40b1-8353-acc70c95f802.json?languageCode=en-US&token=0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ )
+ .expectBadge({ label: 'English (US)', message: isIntegerPercentage })
+
+t.create('Language progress on specific branch')
+ .get(
+ '/5cc34208-0418-40b1-8353-acc70c95f802/Version_1.0.json?languageCode=en-US&token=0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
+ )
+ .expectBadge({ label: 'English (US)', message: isIntegerPercentage })
+
+t.create('Language progress with invalid token')
+ .get(
+ '/1349592f-8d05-4317-9f46-bddc5def11fe/main.json?languageCode=en-US&token=312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ )
+ .intercept(nock =>
+ nock('https://api.localizely.com', {
+ reqheaders: {
+ 'x-api-token':
+ '312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ },
+ })
+ .get('/v1/projects/1349592f-8d05-4317-9f46-bddc5def11fe/status')
+ .query({ branch: 'main' })
+ .reply(403, {
+ errorCode: 'forbidden',
+ errorMessage: 'Tried to access unauthorized project',
+ }),
+ )
+ .expectBadge({ label: 'localized', message: 'not authorized for project' })
+
+t.create('Language progress for unsupported language code')
+ .get(
+ '/1349592f-8d05-4317-9f46-bddc5def11fe/main.json?languageCode=fr&token=312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ )
+ .intercept(nock =>
+ nock('https://api.localizely.com', {
+ reqheaders: {
+ 'x-api-token':
+ '312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ },
+ })
+ .get('/v1/projects/1349592f-8d05-4317-9f46-bddc5def11fe/status')
+ .query({ branch: 'main' })
+ .reply(200, {
+ strings: 100,
+ reviewedProgress: 85,
+ languages: [
+ {
+ langCode: 'zh-Hans-CN',
+ langName: 'Chinese Simplified (CN)',
+ strings: 100,
+ reviewed: 85,
+ reviewedProgress: 85,
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'localized', message: 'Unsupported language' })
+
+t.create('Language progress for supported language code')
+ .get(
+ '/1349592f-8d05-4317-9f46-bddc5def11fe/main.json?languageCode=en-US&token=312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ )
+ .intercept(nock =>
+ nock('https://api.localizely.com', {
+ reqheaders: {
+ 'x-api-token':
+ '312045388bfb4d2591cfe1d60868ea52b63ac6daa6dc406b9bab682f4d9ab715',
+ },
+ })
+ .get('/v1/projects/1349592f-8d05-4317-9f46-bddc5def11fe/status')
+ .query({ branch: 'main' })
+ .reply(200, {
+ strings: 5,
+ reviewedProgress: 60,
+ languages: [
+ {
+ langCode: 'en-US',
+ langName: 'English (US)',
+ strings: 5,
+ reviewed: 3,
+ reviewedProgress: 60,
+ },
+ {
+ langCode: 'de',
+ langName: 'German',
+ strings: 5,
+ reviewed: 3,
+ reviewedProgress: 60,
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'English (US)', message: '60%' })
diff --git a/services/luarocks/luarocks-version-helpers.js b/services/luarocks/luarocks-version-helpers.js
index ee1526adc8fd8..3523f38a5b597 100644
--- a/services/luarocks/luarocks-version-helpers.js
+++ b/services/luarocks/luarocks-version-helpers.js
@@ -24,9 +24,7 @@
* https://github.com/luarocks/luarocks/blob/405ee29ba8444d97646f62e72effeaff2bfe3f79/src/luarocks/search.lua#L182
* https://github.com/luarocks/luarocks/blob/405ee29ba8444d97646f62e72effeaff2bfe3f79/src/luarocks/core/vers.lua#L83
*/
-'use strict'
-
-const { omitv } = require('../text-formatters')
+import { omitv } from '../text-formatters.js'
// Compare two arrays containing split and transformed to
// positive/negative numbers parts of version strings,
@@ -86,8 +84,4 @@ function latestVersion(versions) {
.pop()
}
-module.exports = {
- parseVersion,
- compareVersionLists,
- latestVersion,
-}
+export { parseVersion, compareVersionLists, latestVersion }
diff --git a/services/luarocks/luarocks-version-helpers.spec.js b/services/luarocks/luarocks-version-helpers.spec.js
index 95478d6fa153f..fce50d9acf0ff 100644
--- a/services/luarocks/luarocks-version-helpers.spec.js
+++ b/services/luarocks/luarocks-version-helpers.spec.js
@@ -1,13 +1,11 @@
-'use strict'
-
-const { test, given, forCases } = require('sazerac')
-const {
+import { test, given, forCases } from 'sazerac'
+import {
parseVersion,
compareVersionLists,
latestVersion,
-} = require('./luarocks-version-helpers')
+} from './luarocks-version-helpers.js'
-describe('LuaRocks-specific helpers', function() {
+describe('LuaRocks-specific helpers', function () {
test(compareVersionLists, () => {
forCases([
given([1, 2], [1, 2]),
diff --git a/services/luarocks/luarocks.service.js b/services/luarocks/luarocks.service.js
index 28a3231c267ab..37b8c361ae4c8 100644
--- a/services/luarocks/luarocks.service.js
+++ b/services/luarocks/luarocks.service.js
@@ -1,76 +1,54 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { addv } = require('../text-formatters')
-const { BaseJsonService, NotFound } = require('..')
-const { latestVersion } = require('./luarocks-version-helpers')
+import Joi from 'joi'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { latestVersion } from './luarocks-version-helpers.js'
const schema = Joi.object({
repository: Joi.object()
.pattern(
Joi.string(),
- Joi.object().pattern(Joi.string(), Joi.array().strip())
+ Joi.object().pattern(Joi.string(), Joi.array().strip()),
)
.required(),
}).required()
-module.exports = class Luarocks extends BaseJsonService {
- static get category() {
- return 'version'
- }
+export default class Luarocks extends BaseJsonService {
+ static category = 'version'
- static get route() {
- return {
- base: 'luarocks/v',
- pattern: ':user/:moduleName/:version?',
- }
+ static route = {
+ base: 'luarocks/v',
+ pattern: ':user/:moduleName/:version?',
}
- static get examples() {
- return [
- {
- title: 'LuaRocks',
- namedParams: {
- user: 'mpeterv',
- moduleName: 'luacheck',
- },
- staticPreview: this.render({ version: '0.23.0-1' }),
+ static openApi = {
+ '/luarocks/v/{user}/{moduleName}': {
+ get: {
+ summary: 'LuaRocks',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'mpeterv',
+ },
+ {
+ name: 'moduleName',
+ example: 'luacheck',
+ },
+ ),
},
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'luarocks',
- }
+ },
}
- static render({ version }) {
- // The badge colors are following the heuristic rule where `scm < dev <
- // stable` (e.g., `scm-1` < `dev-1` < `0.1.0-1`).
- let color
- switch (version.slice(0, 3).toLowerCase()) {
- case 'dev':
- color = 'yellow'
- break
- case 'scm':
- case 'cvs':
- color = 'orange'
- break
- default:
- color = 'brightgreen'
- }
-
- return { message: addv(version), color }
+ static defaultBadgeData = {
+ label: 'luarocks',
}
async fetch({ user, moduleName }) {
const { repository } = await this._requestJson({
url: `https://luarocks.org/manifests/${encodeURIComponent(
- user
+ user,
)}/manifest.json`,
schema,
- errorMessages: {
+ httpErrors: {
404: 'user not found',
},
})
@@ -94,6 +72,6 @@ module.exports = class Luarocks extends BaseJsonService {
const versions = Object.keys(moduleInfo)
version = latestVersion(versions)
}
- return this.constructor.render({ version })
+ return renderVersionBadge({ version })
}
}
diff --git a/services/luarocks/luarocks.spec.js b/services/luarocks/luarocks.spec.js
deleted file mode 100644
index ad6479ef7d501..0000000000000
--- a/services/luarocks/luarocks.spec.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const Luarocks = require('./luarocks.service')
-
-test(Luarocks.render, () => {
- given({ version: 'dev-1' }).expect({ message: 'dev-1', color: 'yellow' })
- given({ version: 'scm-1' }).expect({ message: 'scm-1', color: 'orange' })
- given({ version: 'cvs-1' }).expect({ message: 'cvs-1', color: 'orange' })
- given({ version: '0.1-1' }).expect({
- message: 'v0.1-1',
- color: 'brightgreen',
- })
-})
diff --git a/services/luarocks/luarocks.tester.js b/services/luarocks/luarocks.tester.js
index 19910f08fe75b..e82ddcf8eed4f 100644
--- a/services/luarocks/luarocks.tester.js
+++ b/services/luarocks/luarocks.tester.js
@@ -1,18 +1,15 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isLuaVersion = Joi.string()
.regex(/^v\d+\.\d+\.\d+-\d+$/)
.required()
-t.create('version')
- .get('/mpeterv/luacheck.json')
- .expectBadge({
- label: 'luarocks',
- message: isLuaVersion,
- })
+t.create('version').get('/mpeterv/luacheck.json').expectBadge({
+ label: 'luarocks',
+ message: isLuaVersion,
+})
t.create('specified version')
.get('/mpeterv/luacheck/0.9.0-1.json')
diff --git a/services/macports/macports.service.js b/services/macports/macports.service.js
new file mode 100644
index 0000000000000..3af617ed63847
--- /dev/null
+++ b/services/macports/macports.service.js
@@ -0,0 +1,50 @@
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ version: Joi.string().required(),
+}).required()
+
+export default class Macports extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'macports/v',
+ pattern: ':portName',
+ }
+
+ static openApi = {
+ '/macports/v/{portName}': {
+ get: {
+ summary: 'MacPorts Port Version',
+ description:
+ 'Shows the version of a [MacPorts](https://ports.macports.org/) port.',
+ parameters: pathParams({
+ name: 'portName',
+ example: 'git',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'macports',
+ }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async fetch({ portName }) {
+ return this._requestJson({
+ schema,
+ url: `https://ports.macports.org/api/v1/ports/${encodeURIComponent(portName)}/`,
+ })
+ }
+
+ async handle({ portName }) {
+ const data = await this.fetch({ portName })
+ return this.constructor.render({ version: data.version })
+ }
+}
diff --git a/services/macports/macports.tester.js b/services/macports/macports.tester.js
new file mode 100644
index 0000000000000..34a5e1eca556b
--- /dev/null
+++ b/services/macports/macports.tester.js
@@ -0,0 +1,12 @@
+import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('macports (valid)').get('/git.json').expectBadge({
+ label: 'macports',
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
+})
+
+t.create('macports (not found)')
+ .get('/not-a-real-port.json')
+ .expectBadge({ label: 'macports', message: 'not found' })
diff --git a/services/magnumci/magnumci.service.js b/services/magnumci/magnumci.service.js
deleted file mode 100644
index 5a01a0304b38a..0000000000000
--- a/services/magnumci/magnumci.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'build',
- route: {
- base: 'magnumci/ci',
- pattern: ':various+',
- },
- label: 'magnum ci',
- dateAdded: new Date('2018-07-08'),
-})
diff --git a/services/magnumci/magnumci.tester.js b/services/magnumci/magnumci.tester.js
deleted file mode 100644
index d88d56f757622..0000000000000
--- a/services/magnumci/magnumci.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({ id: 'magnumci', title: 'Magnum CI' })
-module.exports = t
-
-t.create('no longer available')
- .get('/ci/96ffb83fa700f069024921b0702e76ff.json')
- .expectBadge({
- label: 'magnum ci',
- message: 'no longer available',
- })
diff --git a/services/maintenance/maintenance.service.js b/services/maintenance/maintenance.service.js
index 8b8997348ab5a..929f9e4ae02d0 100644
--- a/services/maintenance/maintenance.service.js
+++ b/services/maintenance/maintenance.service.js
@@ -1,38 +1,33 @@
-'use strict'
+import { BaseService, pathParams } from '../index.js'
-const { NonMemoryCachingBaseService } = require('..')
+export default class Maintenance extends BaseService {
+ static category = 'other'
-module.exports = class Maintenance extends NonMemoryCachingBaseService {
- static get category() {
- return 'other'
+ static route = {
+ base: 'maintenance',
+ pattern: ':maintained/:year(\\d{4})',
}
- static get route() {
- return {
- base: 'maintenance',
- pattern: ':maintained/:year(\\d{4})',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Maintenance',
- pattern: ':maintained(yes|no)/:year',
- namedParams: {
- maintained: 'yes',
- year: '2019',
- },
- staticPreview: this.render({ isMaintained: false, targetYear: '2018' }),
- keywords: ['maintained'],
+ static openApi = {
+ '/maintenance/{maintained}/{year}': {
+ get: {
+ summary: 'Maintenance',
+ parameters: pathParams(
+ {
+ name: 'maintained',
+ example: 'yes',
+ },
+ {
+ name: 'year',
+ example: '2019',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'maintained',
- }
+ static defaultBadgeData = {
+ label: 'maintained',
}
static render({ isMaintained, isStale, targetYear, message }) {
@@ -44,7 +39,7 @@ module.exports = class Maintenance extends NonMemoryCachingBaseService {
}
return {
- message: `${isStale ? `stale` : 'no!'} (as of ${targetYear})`,
+ message: `${isStale ? 'stale' : 'no!'} (as of ${targetYear})`,
color: isStale ? undefined : 'red',
}
}
diff --git a/services/maintenance/maintenance.spec.js b/services/maintenance/maintenance.spec.js
index c4a5b50504f6f..cb98567746fd1 100644
--- a/services/maintenance/maintenance.spec.js
+++ b/services/maintenance/maintenance.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import Maintenance from './maintenance.service.js'
-const { test, given } = require('sazerac')
-const Maintenance = require('./maintenance.service')
-
-describe('Maintenance', function() {
+describe('Maintenance', function () {
test(Maintenance.prototype.transform, () => {
given({
maintained: 'no',
diff --git a/services/maintenance/maintenance.tester.js b/services/maintenance/maintenance.tester.js
index 04e53ee0bea41..1fd3dee957477 100644
--- a/services/maintenance/maintenance.tester.js
+++ b/services/maintenance/maintenance.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const currentYear = new Date().getUTCFullYear()
diff --git a/services/mastodon/mastodon-follow.service.js b/services/mastodon/mastodon-follow.service.js
index a7487ee1d8bb6..ba601c8ad6246 100644
--- a/services/mastodon/mastodon-follow.service.js
+++ b/services/mastodon/mastodon-follow.service.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { optionalUrl, nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
const schema = Joi.object({
username: Joi.string().required(),
@@ -11,50 +9,43 @@ const schema = Joi.object({
})
const queryParamSchema = Joi.object({
- domain: optionalUrl,
+ domain: Joi.string().optional(),
}).required()
-const documentation = `
-To find your user id, you can use this tool .
-Alternatively you can make a request to https://your.mastodon.server/.well-known/webfinger?resource=acct:{user}@{domain}
-Failing that, you can also visit your profile page, where your user ID will be in the header in a tag like this: <link href='https://your.mastodon.server/api/salmon/{your-user-id}' rel='salmon'>
+const description = `
+To find your user id, you can make a request to \`https://your.mastodon.server/api/v1/accounts/lookup?acct=yourusername\`.
`
-module.exports = class MastodonFollow extends BaseJsonService {
- static get category() {
- return 'social'
- }
+export default class MastodonFollow extends BaseJsonService {
+ static category = 'social'
- static get route() {
- return {
- base: 'mastodon/follow',
- pattern: ':id',
- queryParamSchema,
- }
+ static route = {
+ base: 'mastodon/follow',
+ pattern: ':id',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Mastodon Follow',
- namedParams: {
- id: '26471',
- },
- queryParams: { domain: 'https://mastodon.social' },
- staticPreview: {
- label: 'Follow',
- message: '862',
- style: 'social',
- },
- documentation,
+ static openApi = {
+ '/mastodon/follow/{id}': {
+ get: {
+ summary: 'Mastodon Follow',
+ description,
+ parameters: [
+ pathParam({
+ name: 'id',
+ example: '26471',
+ }),
+ queryParam({
+ name: 'domain',
+ example: 'mastodon.social',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- namedLogo: 'mastodon',
- }
+ static defaultBadgeData = {
+ namedLogo: 'mastodon',
}
static render({ username, followers, domain }) {
@@ -63,8 +54,8 @@ module.exports = class MastodonFollow extends BaseJsonService {
message: metric(followers),
style: 'social',
link: [
- `${domain}/users/${username}/remote_follow`,
- `${domain}/users/${username}/followers`,
+ `https://${domain}/users/${username}`,
+ `https://${domain}/users/${username}/followers`,
],
}
}
@@ -72,13 +63,14 @@ module.exports = class MastodonFollow extends BaseJsonService {
async fetch({ id, domain }) {
return this._requestJson({
schema,
- url: `${domain}/api/v1/accounts/${id}/`,
+ url: `https://${domain}/api/v1/accounts/${id}/`,
})
}
- async handle({ id }, { domain = 'https://mastodon.social' }) {
+ async handle({ id }, { domain = 'mastodon.social' }) {
if (isNaN(id))
throw new NotFound({ prettyMessage: 'invalid user id format' })
+ domain = domain.replace(/^https?:\/\//, '')
const data = await this.fetch({ id, domain })
return this.constructor.render({
username: data.username,
diff --git a/services/mastodon/mastodon-follow.tester.js b/services/mastodon/mastodon-follow.tester.js
index 4f68ec5df905a..c1af88b829867 100644
--- a/services/mastodon/mastodon-follow.tester.js
+++ b/services/mastodon/mastodon-follow.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Followers - default domain')
.get('/26471.json')
@@ -9,7 +8,7 @@ t.create('Followers - default domain')
label: 'follow @wilkie',
message: isMetric,
link: [
- 'https://mastodon.social/users/wilkie/remote_follow',
+ 'https://mastodon.social/users/wilkie',
'https://mastodon.social/users/wilkie/followers',
],
})
@@ -29,12 +28,23 @@ t.create('Followers - default domain - invalid user ID (id not in use)')
})
t.create('Followers - alternate domain')
+ .get('/2214.json?domain=mastodon.xyz')
+ .expectBadge({
+ label: 'follow @PhotonQyv',
+ message: isMetric,
+ link: [
+ 'https://mastodon.xyz/users/PhotonQyv',
+ 'https://mastodon.xyz/users/PhotonQyv/followers',
+ ],
+ })
+
+t.create('Followers - alternate domain legacy')
.get('/2214.json?domain=https%3A%2F%2Fmastodon.xyz')
.expectBadge({
label: 'follow @PhotonQyv',
message: isMetric,
link: [
- 'https://mastodon.xyz/users/PhotonQyv/remote_follow',
+ 'https://mastodon.xyz/users/PhotonQyv',
'https://mastodon.xyz/users/PhotonQyv/followers',
],
})
diff --git a/services/matrix/matrix.service.js b/services/matrix/matrix.service.js
index 8d63d665a17da..60b22ed730970 100644
--- a/services/matrix/matrix.service.js
+++ b/services/matrix/matrix.service.js
@@ -1,10 +1,19 @@
-'use strict'
+import Joi from 'joi'
+import {
+ BaseJsonService,
+ InvalidParameter,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
-const Joi = require('@hapi/joi')
-const { BaseJsonService, InvalidParameter } = require('..')
+const fetchModeEnum = ['guest', 'summary']
const queryParamSchema = Joi.object({
server_fqdn: Joi.string().hostname(),
+ fetchMode: Joi.string()
+ .valid(...fetchModeEnum)
+ .default('guest'),
}).required()
const matrixRegisterSchema = Joi.object({
@@ -23,75 +32,80 @@ const matrixStateSchema = Joi.array()
}).required(),
type: Joi.string().required(),
sender: Joi.string().required(),
- state_key: Joi.string()
- .allow('')
- .required(),
- })
+ state_key: Joi.string().allow('').required(),
+ }),
)
.required()
-const documentation = `
-
- In order for this badge to work, the host of your room must allow guest accounts or dummy accounts to register, and the room must be world readable (chat history visible to anyone).
-
- The following steps will show you how to setup the badge URL using the Riot.im Matrix client.
-
-
- Select the desired room inside the Riot.im client
- Click on the room settings button (gear icon) located near the top right of the client
- Scroll to the very bottom of the settings page and look under the Addresses section
- You should see one or more room addresses (or aliases), which can be easily identified with their starting hash (#) character (ex: #twim:matrix.org)
- If there is no address for this room, add one under Local addresses for this room
- Remove the starting hash character (#)
- The final badge URL should look something like this /matrix/twim:matrix.org.svg
-
-
- Some Matrix homeservers don't hold a server name matching where they live (e.g. if the homeserver example.com that created the room alias #mysuperroom:example.com lives at matrix.example.com).
-
- If that is the case of the homeserver that created the room alias used for generating the badge, you will need to add the server's FQDN (fully qualified domain name) as a query parameter.
-
- The final badge URL should then look something like this /matrix/mysuperroom:example.com.svg?server_fqdn=matrix.example.com.
-
- `
-
-module.exports = class Matrix extends BaseJsonService {
- static get category() {
- return 'chat'
- }
+const matrixSummarySchema = Joi.object({
+ num_joined_members: nonNegativeInteger,
+}).required()
- static get route() {
- return {
- base: 'matrix',
- pattern: ':roomAlias',
- queryParamSchema,
- }
+const description = `
+In order for this badge to work, the host of your room must allow guest accounts or dummy accounts to register, and the room must be world readable (chat history visible to anyone).
+
+Alternatively access via the experimental summary endpoint ([MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)) can be configured with the query parameter fetchMode for less server load and better performance, if supported by the homeserver
+For the matrix.org homeserver fetchMode is hard-coded to summary.
+
+The following steps will show you how to setup the badge URL using the Element Matrix client.
+
+
+ Select the desired room inside the Element client
+ Click on the room settings button (gear icon) located near the top right of the client
+ Scroll to the very bottom of the settings page and look under the Addresses section
+ You should see one or more room addresses (or aliases), which can be easily identified with their starting hash (#) character (ex: #twim:matrix.org)
+ If there is no address for this room, add one under Local addresses for this room
+ Remove the starting hash character (#)
+ The final badge URL should look something like this /matrix/twim:matrix.org.svg
+
+
+Some Matrix homeservers don't hold a server name matching where they live (e.g. if the homeserver example.com that created the room alias #mysuperroom:example.com lives at matrix.example.com).
+
+If that is the case of the homeserver that created the room alias used for generating the badge, you will need to add the server's FQDN (fully qualified domain name) as a query parameter.
+
+The final badge URL should then look something like this /matrix/mysuperroom:example.com.svg?server_fqdn=matrix.example.com.
+`
+
+export default class Matrix extends BaseJsonService {
+ static category = 'chat'
+
+ static route = {
+ base: 'matrix',
+ pattern: ':roomAlias',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Matrix',
- namedParams: { roomAlias: 'twim:matrix.org' },
- staticPreview: this.render({ members: 42 }),
- documentation,
- },
- {
- title: 'Matrix',
- namedParams: { roomAlias: 'twim:matrix.org' },
- queryParams: { server_fqdn: 'matrix.org' },
- staticPreview: this.render({ members: 42 }),
- documentation,
+ static openApi = {
+ '/matrix/{roomAlias}': {
+ get: {
+ summary: 'Matrix',
+ description,
+ parameters: [
+ pathParam({
+ name: 'roomAlias',
+ example: 'twim:matrix.org',
+ }),
+ queryParam({
+ name: 'server_fqdn',
+ example: 'matrix.org',
+ }),
+ queryParam({
+ name: 'fetchMode',
+ example: 'guest',
+ description: `guest configures guest authentication while summary configures usage of the experimental "summary" endpoint ([MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)). If not specified, the default fetch mode is guest (except for matrix.org).`,
+ schema: {
+ type: 'string',
+ enum: fetchModeEnum,
+ },
+ }),
+ ],
},
- ]
+ },
}
- static get _cacheLength() {
- return 30
- }
+ static _cacheLength = 14400
- static get defaultBadgeData() {
- return { label: 'chat' }
- }
+ static defaultBadgeData = { label: 'chat' }
static render({ members }) {
return {
@@ -120,7 +134,7 @@ module.exports = class Matrix extends BaseJsonService {
schema: matrixRegisterSchema,
options: {
method: 'POST',
- qs: guest
+ searchParams: guest
? {
kind: 'guest',
}
@@ -130,10 +144,9 @@ module.exports = class Matrix extends BaseJsonService {
auth: { type: 'm.login.dummy' },
}),
},
- errorMessages: {
+ httpErrors: {
401: 'auth failed',
403: 'guests not allowed',
- 429: 'rate limited by remote server',
},
})
}
@@ -141,54 +154,53 @@ module.exports = class Matrix extends BaseJsonService {
async lookupRoomAlias({ host, roomAlias, accessToken }) {
return this._requestJson({
url: `https://${host}/_matrix/client/r0/directory/room/${encodeURIComponent(
- `#${roomAlias}`
+ `#${roomAlias}`,
)}`,
schema: matrixAliasLookupSchema,
options: {
- qs: {
+ searchParams: {
access_token: accessToken,
},
},
- errorMessages: {
+ httpErrors: {
401: 'bad auth token',
404: 'room not found',
- 429: 'rate limited by remote server',
},
})
}
- async fetch({ roomAlias, serverFQDN }) {
- let host
- if (serverFQDN === undefined) {
- const splitAlias = roomAlias.split(':')
- // A room alias can either be in the form #localpart:server or
- // #localpart:server:port.
- switch (splitAlias.length) {
- case 2:
- host = splitAlias[1]
- break
- case 3:
- host = `${splitAlias[1]}:${splitAlias[2]}`
- break
- default:
- throw new InvalidParameter({ prettyMessage: 'invalid alias' })
- }
- } else {
- host = serverFQDN
- }
+ async fetchSummary({ host, roomAlias }) {
+ const data = await this._requestJson({
+ url: `https://${host}/_matrix/client/unstable/im.nheko.summary/rooms/%23${encodeURIComponent(
+ roomAlias,
+ )}/summary`,
+ schema: matrixSummarySchema,
+ httpErrors: {
+ 400: 'unknown request',
+ 404: 'room or endpoint not found',
+ },
+ })
+ return data.num_joined_members
+ }
+
+ async fetchGuest({ host, roomAlias }) {
const accessToken = await this.retrieveAccessToken({ host })
- const lookup = await this.lookupRoomAlias({ host, roomAlias, accessToken })
+ const lookup = await this.lookupRoomAlias({
+ host,
+ roomAlias,
+ accessToken,
+ })
const data = await this._requestJson({
url: `https://${host}/_matrix/client/r0/rooms/${encodeURIComponent(
- lookup.room_id
+ lookup.room_id,
)}/state`,
schema: matrixStateSchema,
options: {
- qs: {
+ searchParams: {
access_token: accessToken,
},
},
- errorMessages: {
+ httpErrors: {
400: 'unknown request',
401: 'bad auth token',
403: 'room not world readable or is invalid',
@@ -199,13 +211,41 @@ module.exports = class Matrix extends BaseJsonService {
m =>
m.type === 'm.room.member' &&
m.sender === m.state_key &&
- m.content.membership === 'join'
+ m.content.membership === 'join',
).length
: 0
}
- async handle({ roomAlias }, { server_fqdn: serverFQDN }) {
- const members = await this.fetch({ roomAlias, serverFQDN })
+ async fetch({ roomAlias, serverFQDN, fetchMode }) {
+ let host
+ if (serverFQDN === undefined) {
+ const splitAlias = roomAlias.split(':')
+ // A room alias can either be in the form #localpart:server or
+ // #localpart:server:port.
+ switch (splitAlias.length) {
+ case 2:
+ host = splitAlias[1]
+ break
+ case 3:
+ host = `${splitAlias[1]}:${splitAlias[2]}`
+ break
+ default:
+ throw new InvalidParameter({ prettyMessage: 'invalid alias' })
+ }
+ } else {
+ host = serverFQDN
+ }
+ if (host.toLowerCase() === 'matrix.org' || fetchMode === 'summary') {
+ // summary endpoint (default for matrix.org)
+ return await this.fetchSummary({ host, roomAlias })
+ } else {
+ // guest access
+ return await this.fetchGuest({ host, roomAlias })
+ }
+ }
+
+ async handle({ roomAlias }, { server_fqdn: serverFQDN, fetchMode }) {
+ const members = await this.fetch({ roomAlias, serverFQDN, fetchMode })
return this.constructor.render({ members })
}
}
diff --git a/services/matrix/matrix.tester.js b/services/matrix/matrix.tester.js
index 43035127b620a..36359d07dfcda 100644
--- a/services/matrix/matrix.tester.js
+++ b/services/matrix/matrix.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('get room state as guest')
.get('/ALIAS:DUMMY.dumb.json')
@@ -12,19 +11,19 @@ t.create('get room state as guest')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN',
)
.reply(
200,
@@ -65,8 +64,8 @@ t.create('get room state as guest')
membership: 'fake room',
},
},
- ])
- )
+ ]),
+ ),
)
.expectBadge({
label: 'chat',
@@ -84,26 +83,26 @@ t.create('get room state as member (backup method)')
JSON.stringify({
errcode: 'M_GUEST_ACCESS_FORBIDDEN',
error: 'Guest access not allowed',
- })
+ }),
)
.post('/_matrix/client/r0/register')
.reply(
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN',
)
.reply(
200,
@@ -144,8 +143,8 @@ t.create('get room state as member (backup method)')
membership: 'fake room',
},
},
- ])
- )
+ ]),
+ ),
)
.expectBadge({
label: 'chat',
@@ -153,6 +152,26 @@ t.create('get room state as member (backup method)')
color: 'brightgreen',
})
+t.create('get room summary')
+ .get('/ALIAS:DUMMY.dumb.json?fetchMode=summary')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb/summary',
+ )
+ .reply(
+ 200,
+ JSON.stringify({
+ num_joined_members: 4,
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'chat',
+ message: '4 users',
+ color: 'brightgreen',
+ })
+
t.create('bad server or connection')
.get('/ALIAS:DUMMY.dumb.json')
.networkOff()
@@ -171,27 +190,27 @@ t.create('non-world readable room')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN',
)
.reply(
403,
JSON.stringify({
errcode: 'M_GUEST_ACCESS_FORBIDDEN',
error: 'Guest access not allowed',
- })
- )
+ }),
+ ),
)
.expectBadge({
label: 'chat',
@@ -208,18 +227,18 @@ t.create('invalid token')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
401,
JSON.stringify({
errcode: 'M_UNKNOWN_TOKEN',
error: 'Unrecognised access token.',
- })
- )
+ }),
+ ),
)
.expectBadge({
label: 'chat',
@@ -236,27 +255,48 @@ t.create('unknown request')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN',
)
.reply(
400,
JSON.stringify({
errcode: 'M_UNRECOGNIZED',
error: 'Unrecognized request',
- })
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'chat',
+ message: 'unknown request',
+ color: 'lightgrey',
+ })
+
+t.create('unknown summary request')
+ .get('/ALIAS:DUMMY.dumb.json?fetchMode=summary')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb/summary',
)
+ .reply(
+ 400,
+ JSON.stringify({
+ errcode: 'M_UNRECOGNIZED',
+ error: 'Unrecognized request',
+ }),
+ ),
)
.expectBadge({
label: 'chat',
@@ -273,18 +313,18 @@ t.create('unknown alias')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
404,
JSON.stringify({
errcode: 'M_NOT_FOUND',
error: 'Room alias #ALIAS%3ADUMMY.dumb not found.',
- })
- )
+ }),
+ ),
)
.expectBadge({
label: 'chat',
@@ -292,14 +332,33 @@ t.create('unknown alias')
color: 'red',
})
-t.create('invalid alias')
- .get('/ALIASDUMMY.dumb.json')
+t.create('unknown summary alias')
+ .get('/ALIAS:DUMMY.dumb.json?fetchMode=summary')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb/summary',
+ )
+ .reply(
+ 404,
+ JSON.stringify({
+ errcode: 'M_NOT_FOUND',
+ error: 'Room alias #ALIAS%3ADUMMY.dumb not found.',
+ }),
+ ),
+ )
.expectBadge({
label: 'chat',
- message: 'invalid alias',
+ message: 'room or endpoint not found',
color: 'red',
})
+t.create('invalid alias').get('/ALIASDUMMY.dumb.json').expectBadge({
+ label: 'chat',
+ message: 'invalid alias',
+ color: 'red',
+})
+
t.create('server uses a custom port')
.get('/ALIAS:DUMMY.dumb:5555.json')
.intercept(nock =>
@@ -309,19 +368,19 @@ t.create('server uses a custom port')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb%3A5555?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb%3A5555?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb:5555',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb%3A5555/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb%3A5555/state?access_token=TOKEN',
)
.reply(
200,
@@ -362,8 +421,8 @@ t.create('server uses a custom port')
membership: 'fake room',
},
},
- ])
- )
+ ]),
+ ),
)
.expectBadge({
label: 'chat',
@@ -371,6 +430,26 @@ t.create('server uses a custom port')
color: 'brightgreen',
})
+t.create('server uses a custom port for summary')
+ .get('/ALIAS:DUMMY.dumb:5555.json?fetchMode=summary')
+ .intercept(nock =>
+ nock('https://DUMMY.dumb:5555/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb%3A5555/summary',
+ )
+ .reply(
+ 200,
+ JSON.stringify({
+ num_joined_members: 4,
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'chat',
+ message: '4 users',
+ color: 'brightgreen',
+ })
+
t.create('specify the homeserver fqdn')
.get('/ALIAS:DUMMY.dumb.json?server_fqdn=matrix.DUMMY.dumb')
.intercept(nock =>
@@ -380,19 +459,19 @@ t.create('specify the homeserver fqdn')
200,
JSON.stringify({
access_token: 'TOKEN',
- })
+ }),
)
.get(
- '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN'
+ '/_matrix/client/r0/directory/room/%23ALIAS%3ADUMMY.dumb?access_token=TOKEN',
)
.reply(
200,
JSON.stringify({
room_id: 'ROOM:DUMMY.dumb',
- })
+ }),
)
.get(
- '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN'
+ '/_matrix/client/r0/rooms/ROOM%3ADUMMY.dumb/state?access_token=TOKEN',
)
.reply(
200,
@@ -433,8 +512,8 @@ t.create('specify the homeserver fqdn')
membership: 'fake room',
},
},
- ])
- )
+ ]),
+ ),
)
.expectBadge({
label: 'chat',
@@ -442,9 +521,56 @@ t.create('specify the homeserver fqdn')
color: 'brightgreen',
})
-t.create('test on real matrix room for API compliance')
+t.create('specify the homeserver fqdn for summary')
+ .get('/ALIAS:DUMMY.dumb.json?server_fqdn=matrix.DUMMY.dumb&fetchMode=summary')
+ .intercept(nock =>
+ nock('https://matrix.DUMMY.dumb/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb/summary',
+ )
+ .reply(
+ 200,
+ JSON.stringify({
+ num_joined_members: 4,
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'chat',
+ message: '4 users',
+ color: 'brightgreen',
+ })
+
+t.create('test fetchMode=guest is ignored for matrix.org')
+ .get('/ALIAS:DUMMY.dumb.json?server_fqdn=matrix.org&fetchMode=guest')
+ .intercept(nock =>
+ nock('https://matrix.org/')
+ .get(
+ '/_matrix/client/unstable/im.nheko.summary/rooms/%23ALIAS%3ADUMMY.dumb/summary',
+ )
+ .reply(
+ 200,
+ JSON.stringify({
+ num_joined_members: 4,
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'chat',
+ message: '4 users',
+ color: 'brightgreen',
+ })
+
+t.create('test on real matrix room for guest API compliance')
+ .get('/twim:matrix.org.json?fetchMode=guest')
+ .expectBadge({
+ label: 'chat',
+ message: Joi.string().regex(/^[0-9]+ users$/),
+ color: 'brightgreen',
+ })
+
+t.create('test on real matrix room for summary API compliance')
.get('/twim:matrix.org.json')
- .timeout(10000)
.expectBadge({
label: 'chat',
message: Joi.string().regex(/^[0-9]+ users$/),
diff --git a/services/maven-central/maven-central-base.js b/services/maven-central/maven-central-base.js
new file mode 100644
index 0000000000000..c5bd1489f4bc1
--- /dev/null
+++ b/services/maven-central/maven-central-base.js
@@ -0,0 +1,13 @@
+import { BaseXmlService } from '../index.js'
+
+export default class MavenCentralBase extends BaseXmlService {
+ async fetch({ groupId, artifactId, schema }) {
+ const group = encodeURIComponent(groupId).replace(/\./g, '/')
+ const artifact = encodeURIComponent(artifactId)
+ return this._requestXml({
+ schema,
+ url: `https://repo1.maven.org/maven2/${group}/${artifact}/maven-metadata.xml`,
+ httpErrors: { 404: 'artifact not found' },
+ })
+ }
+}
diff --git a/services/maven-central/maven-central-last-update.service.js b/services/maven-central/maven-central-last-update.service.js
new file mode 100644
index 0000000000000..6632de4fade96
--- /dev/null
+++ b/services/maven-central/maven-central-last-update.service.js
@@ -0,0 +1,51 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { parseDate, renderDateBadge } from '../date.js'
+import { nonNegativeInteger } from '../validators.js'
+import MavenCentralBase from './maven-central-base.js'
+
+const updateResponseSchema = Joi.object({
+ metadata: Joi.object({
+ versioning: Joi.object({
+ lastUpdated: nonNegativeInteger,
+ }).required(),
+ }).required(),
+}).required()
+
+export default class MavenCentralLastUpdate extends MavenCentralBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'maven-central/last-update',
+ pattern: ':groupId/:artifactId',
+ }
+
+ static openApi = {
+ '/maven-central/last-update/{groupId}/{artifactId}': {
+ get: {
+ summary: 'Maven Central Last Update',
+ parameters: pathParams(
+ { name: 'groupId', example: 'com.google.guava' },
+ { name: 'artifactId', example: 'guava' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ async handle({ groupId, artifactId }) {
+ const { metadata } = await this.fetch({
+ groupId,
+ artifactId,
+ schema: updateResponseSchema,
+ })
+
+ const date = parseDate(
+ String(metadata.versioning.lastUpdated),
+ 'YYYYMMDDHHmmss',
+ )
+
+ return renderDateBadge(date)
+ }
+}
diff --git a/services/maven-central/maven-central-last-update.tester.js b/services/maven-central/maven-central-last-update.tester.js
new file mode 100644
index 0000000000000..46fe38b7f22a0
--- /dev/null
+++ b/services/maven-central/maven-central-last-update.tester.js
@@ -0,0 +1,15 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('last update date').get('/com.google.guava/guava.json').expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+})
+
+t.create('last update when artifact not found')
+ .get('/com.fail.test/this-does-not-exist.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'artifact not found',
+ })
diff --git a/services/maven-central/maven-central.service.js b/services/maven-central/maven-central.service.js
index 6a4911ac01f36..402d261c5ee99 100644
--- a/services/maven-central/maven-central.service.js
+++ b/services/maven-central/maven-central.service.js
@@ -1,91 +1,36 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseXmlService, NotFound } = require('..')
-
-const schema = Joi.object({
- metadata: Joi.object({
- versioning: Joi.object({
- versions: Joi.object({
- version: Joi.array()
- .items(Joi.string().required())
- .single()
- .required(),
- }).required(),
- }).required(),
- }).required(),
-}).required()
-
-module.exports = class MavenCentral extends BaseXmlService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'maven-central/v',
- pattern: ':groupId/:artifactId/:versionPrefix?',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Maven Central',
- pattern: ':groupId/:artifactId',
- namedParams: {
- groupId: 'org.apache.maven',
- artifactId: 'apache-maven',
- },
- staticPreview: {
- label: 'maven-central',
- message: 'v3.6.0',
- color: 'blue',
- },
- },
- {
- title: 'Maven Central with version prefix filter',
- pattern: ':groupId/:artifactId/:versionPrefix',
- namedParams: {
- groupId: 'org.apache.maven',
- artifactId: 'apache-maven',
- versionPrefix: '2',
- },
- staticPreview: {
- label: 'maven-central',
- message: 'v2.2.1',
- color: 'blue',
- },
+import { redirector, pathParam } from '../index.js'
+import { commonParams } from '../maven-metadata/maven-metadata.js'
+
+export default redirector({
+ category: 'version',
+ isRetired: false,
+ route: {
+ base: 'maven-central/v',
+ pattern: ':groupId/:artifactId/:versionPrefix?',
+ },
+ openApi: {
+ '/maven-central/v/{groupId}/{artifactId}': {
+ get: {
+ summary: 'Maven Central Version',
+ parameters: [
+ pathParam({ name: 'groupId', example: 'com.google.guava' }),
+ pathParam({ name: 'artifactId', example: 'guava' }),
+ ...commonParams,
+ ],
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'maven-central' }
- }
-
- async fetch({ groupId, artifactId }) {
+ },
+ },
+ transformPath: () => '/maven-metadata/v',
+ transformQueryParams: ({ groupId, artifactId, versionPrefix }) => {
const group = encodeURIComponent(groupId).replace(/\./g, '/')
const artifact = encodeURIComponent(artifactId)
- const url = `https://repo1.maven.org/maven2/${group}/${artifact}/maven-metadata.xml`
- return this._requestXml({
- schema,
- url,
- parserOptions: { parseNodeValue: false },
- })
- }
-
- async handle({ groupId, artifactId, versionPrefix }) {
- const data = await this.fetch({ groupId, artifactId })
- const versions = data.metadata.versioning.versions.version.reverse()
- let version = versions[0]
- if (versionPrefix !== undefined) {
- version = versions.filter(v => v.toString().startsWith(versionPrefix))[0]
- // if the filter returned no results, throw a NotFound
- if (version === undefined)
- throw new NotFound({ prettyMessage: 'version prefix not found' })
+ const metadataUrl = `https://repo1.maven.org/maven2/${group}/${artifact}/maven-metadata.xml`
+ return {
+ metadataUrl,
+ label: 'maven-central',
+ versionPrefix,
}
- return renderVersionBadge({ version })
- }
-}
+ },
+ overrideTransformedQueryParams: true,
+ dateAdded: new Date('2021-06-12'),
+})
diff --git a/services/maven-central/maven-central.tester.js b/services/maven-central/maven-central.tester.js
index cde5e050cc004..78e5a9676abea 100644
--- a/services/maven-central/maven-central.tester.js
+++ b/services/maven-central/maven-central.tester.js
@@ -1,53 +1,18 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('latest version')
+t.create('latest version redirection')
.get('/com.github.fabriziocucci/yacl4j.json') // http://repo1.maven.org/maven2/com/github/fabriziocucci/yacl4j/
- .expectBadge({
- label: 'maven-central',
- message: isVPlusDottedVersionNClausesWithOptionalSuffix,
- })
+ .expectRedirect(
+ `/maven-metadata/v.json?metadataUrl=${encodeURIComponent(
+ 'https://repo1.maven.org/maven2/com/github/fabriziocucci/yacl4j/maven-metadata.xml',
+ )}&label=maven-central`,
+ )
-t.create('latest 0.8 version')
+t.create('latest 0.8 version redirection')
.get('/com.github.fabriziocucci/yacl4j/0.8.json') // http://repo1.maven.org/maven2/com/github/fabriziocucci/yacl4j/
- .expectBadge({
- label: 'maven-central',
- message: isVPlusDottedVersionNClausesWithOptionalSuffix,
- })
-
-t.create('inexistent artifact')
- .get('/inexistent-group-id/inexistent-artifact-id.json')
- .expectBadge({ label: 'maven-central', message: 'not found' })
-
-t.create('inexistent version prefix')
- .get('/com.github.fabriziocucci/yacl4j/99.json')
- .expectBadge({ label: 'maven-central', message: 'version prefix not found' })
-
-t.create('version ending with zero')
- .get('/mocked-group-id/mocked-artifact-id.json')
- .intercept(nock =>
- nock('https://repo1.maven.org/maven2')
- .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
- .reply(
- 200,
- `
-
- mocked-group-id
- mocked-artifact-id
-
- 1.30
- 1.30
-
- 1.30
-
- 20190902002617
-
-
- `
- )
+ .expectRedirect(
+ `/maven-metadata/v.json?metadataUrl=${encodeURIComponent(
+ 'https://repo1.maven.org/maven2/com/github/fabriziocucci/yacl4j/maven-metadata.xml',
+ )}&label=maven-central&versionPrefix=0.8`,
)
- .expectBadge({ label: 'maven-central', message: 'v1.30' })
diff --git a/services/maven-metadata/maven-metadata-redirect.service.js b/services/maven-metadata/maven-metadata-redirect.service.js
index c713ddd35028b..a7cffcca7c577 100644
--- a/services/maven-metadata/maven-metadata-redirect.service.js
+++ b/services/maven-metadata/maven-metadata-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = redirector({
+export default redirector({
category: 'version',
route: {
base: 'maven-metadata/v',
diff --git a/services/maven-metadata/maven-metadata-redirect.tester.js b/services/maven-metadata/maven-metadata-redirect.tester.js
index 4694203de109e..0f34524979740 100644
--- a/services/maven-metadata/maven-metadata-redirect.tester.js
+++ b/services/maven-metadata/maven-metadata-redirect.tester.js
@@ -1,23 +1,22 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('maven metadata (badge extension)')
.get(
- '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml.json'
+ '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml.json',
)
.expectRedirect(
`/maven-metadata/v.json?metadataUrl=${encodeURIComponent(
- 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml'
- )}`
+ 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml',
+ )}`,
)
t.create('maven metadata (no badge extension)')
.get(
- '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml'
+ '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml',
)
.expectRedirect(
`/maven-metadata/v.svg?metadataUrl=${encodeURIComponent(
- 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml'
- )}`
+ 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml',
+ )}`,
)
diff --git a/services/maven-metadata/maven-metadata.js b/services/maven-metadata/maven-metadata.js
new file mode 100644
index 0000000000000..b217704ee760d
--- /dev/null
+++ b/services/maven-metadata/maven-metadata.js
@@ -0,0 +1,29 @@
+import { queryParams } from '../index.js'
+
+const strategyEnum = ['highestVersion', 'releaseProperty', 'latestProperty']
+
+const strategyDocs = `The strategy used to determine the version that will be shown
+
+ highestVersion - sort versions using Maven's ComparableVersion semantics and pick the highest (default)
+ releaseProperty - use the "release" metadata property
+ latestProperty - use the "latest" metadata property
+ `
+
+const filterDocs = `
+The filter param can be used to apply a filter to the
+project's versions before selecting the latest from the list.
+Two constructs are available: * is a wildcard matching zero
+or more characters, and if the pattern starts with a !,
+the whole pattern is negated.
+`
+const commonParams = queryParams(
+ {
+ name: 'strategy',
+ description: strategyDocs,
+ schema: { type: 'string', enum: strategyEnum },
+ example: 'highestVersion',
+ },
+ { name: 'filter', example: '*beta', description: filterDocs },
+)
+
+export { strategyEnum, commonParams }
diff --git a/services/maven-metadata/maven-metadata.service.js b/services/maven-metadata/maven-metadata.service.js
index 66a2815735cb5..e77f1eb7eab93 100644
--- a/services/maven-metadata/maven-metadata.service.js
+++ b/services/maven-metadata/maven-metadata.service.js
@@ -1,70 +1,150 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { renderVersionBadge } = require('../version')
-const { BaseXmlService } = require('..')
+import Joi from 'joi'
+import { matcher } from 'matcher'
+import { compare } from 'mvncmp'
+import { url } from '../validators.js'
+import { renderVersionBadge } from '../version.js'
+import {
+ BaseXmlService,
+ InvalidParameter,
+ InvalidResponse,
+ NotFound,
+ queryParams,
+} from '../index.js'
+import { strategyEnum, commonParams } from './maven-metadata.js'
const queryParamSchema = Joi.object({
- metadataUrl: optionalUrl.required(),
-}).required()
+ metadataUrl: url,
+ // versionPrefix and versionSuffix params are undocumented
+ // but supported for legacy compatibility
+ versionPrefix: Joi.string().optional(),
+ versionSuffix: Joi.string().optional(),
+ // filter is now the preferred way to do this
+ filter: Joi.string().optional(),
+ strategy: Joi.string()
+ .valid(...strategyEnum)
+ .default('highestVersion')
+ .optional(),
+})
+ // versionPrefix/Suffix are invalid
+ // when combined with filter
+ .oxor('filter', 'versionPrefix')
+ .oxor('filter', 'versionSuffix')
const schema = Joi.object({
metadata: Joi.object({
versioning: Joi.object({
+ latest: Joi.string(),
+ release: Joi.string(),
versions: Joi.object({
- version: Joi.array()
- .items(Joi.string().required())
- .single()
- .required(),
- }).required(),
+ version: Joi.array().items(Joi.string()).single(),
+ }),
}).required(),
}).required(),
}).required()
-module.exports = class MavenMetadata extends BaseXmlService {
- static get category() {
- return 'version'
- }
+export default class MavenMetadata extends BaseXmlService {
+ static category = 'version'
- static get route() {
- return {
- base: 'maven-metadata',
- pattern: 'v',
- queryParamSchema,
- }
+ static route = {
+ base: 'maven-metadata',
+ pattern: 'v',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Maven metadata URL',
- namedParams: {},
- queryParams: {
- metadataUrl:
- 'https://repo1.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml',
- },
- staticPreview: renderVersionBadge({ version: '2.8.5' }),
+ static openApi = {
+ '/maven-metadata/v': {
+ get: {
+ summary: 'Maven metadata URL',
+ parameters: queryParams(
+ {
+ name: 'metadataUrl',
+ example:
+ 'https://repo1.maven.org/maven2/com/google/guava/guava/maven-metadata.xml',
+ required: true,
+ },
+ ...commonParams,
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'maven' }
+ static defaultBadgeData = {
+ label: 'maven',
}
async fetch({ metadataUrl }) {
return this._requestXml({
schema,
url: metadataUrl,
- parserOptions: { parseNodeValue: false },
+ parserOptions: { parseTagValue: false },
})
}
- async handle(_namedParams, { metadataUrl }) {
+ static applyFilter({ versions, filter }) {
+ if (!filter) {
+ return versions
+ }
+ return matcher(versions, filter)
+ }
+
+ static getLatestVersion({ data, strategy, filter }) {
+ if (strategy === 'latestProperty') {
+ if (data.metadata.versioning.latest === undefined) {
+ throw new InvalidResponse({
+ prettyMessage: "property 'latest' not found",
+ })
+ }
+ return data.metadata.versioning.latest
+ } else if (strategy === 'releaseProperty') {
+ if (data.metadata.versioning.release === undefined) {
+ throw new InvalidResponse({
+ prettyMessage: "property 'release' not found",
+ })
+ }
+ return data.metadata.versioning.release
+ } else if (strategy === 'highestVersion') {
+ if (
+ data.metadata.versioning.versions?.version === undefined ||
+ data.metadata.versioning.versions?.version?.length === 0
+ ) {
+ throw new InvalidResponse({
+ prettyMessage: 'no versions found',
+ })
+ }
+ const versions = this.applyFilter({
+ versions: data.metadata.versioning.versions.version,
+ filter,
+ })
+ if (versions.length === 0) {
+ throw new NotFound({ prettyMessage: 'no matching versions found' })
+ }
+ return versions.sort(compare).reverse()[0]
+ }
+ throw new InvalidParameter({ prettyMessage: 'unknown strategy' })
+ }
+
+ async handle(
+ _namedParams,
+ { metadataUrl, versionPrefix, versionSuffix, strategy, filter },
+ ) {
+ if (
+ (versionPrefix !== undefined ||
+ versionSuffix !== undefined ||
+ filter !== undefined) &&
+ strategy !== 'highestVersion'
+ ) {
+ throw new InvalidParameter({
+ prettyMessage: `filter is not valid with strategy ${strategy}`,
+ })
+ }
+
+ if (versionPrefix !== undefined || versionSuffix !== undefined) {
+ filter = `${versionPrefix || ''}*${versionSuffix || ''}`
+ }
+
const data = await this.fetch({ metadataUrl })
return renderVersionBadge({
- version: data.metadata.versioning.versions.version.slice(-1)[0],
+ version: this.constructor.getLatestVersion({ data, strategy, filter }),
})
}
}
diff --git a/services/maven-metadata/maven-metadata.tester.js b/services/maven-metadata/maven-metadata.tester.js
index 4eda201f4df38..024de93b16f8a 100644
--- a/services/maven-metadata/maven-metadata.tester.js
+++ b/services/maven-metadata/maven-metadata.tester.js
@@ -1,46 +1,185 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
+const mockMetaData = `
+
+ mocked-group-id
+ mocked-artifact-id
+
+ 1.31-beta1
+ 1.30
+
+ 1.0
+ 1.31-rc1
+ 1.31-beta1
+ 1.30
+
+ 20190902002617
+
+
+`
t.create('valid maven-metadata.xml uri')
.get(
- '/v.json?metadataUrl=https://repo1.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml'
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml',
)
.expectBadge({
label: 'maven',
message: isVPlusDottedVersionAtLeastOne,
})
-t.create('version ending with zero')
+t.create('release strategy')
.get(
- '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml'
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=releaseProperty',
)
.intercept(nock =>
nock('https://repo1.maven.org/maven2')
.get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
- .reply(
- 200,
- `
-
- mocked-group-id
- mocked-artifact-id
-
- 1.30
- 1.30
-
- 1.30
-
- 20190902002617
-
-
- `
- )
+ .reply(200, mockMetaData),
)
.expectBadge({ label: 'maven', message: 'v1.30' })
+t.create('latest strategy')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=latestProperty',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-beta1' })
+
+t.create('comparableVersion strategy')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=highestVersion',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-rc1' })
+
+t.create('comparableVersion strategy with versionPrefix')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&versionPrefix=1.31',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-rc1' })
+
+t.create('comparableVersion strategy with versionSuffix')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&versionSuffix=1',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-rc1' })
+
+t.create('comparableVersion strategy with versionPrefix and versionSuffix')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&versionPrefix=1.31&versionSuffix=1',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-rc1' })
+
+t.create('comparableVersion strategy with filter')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&filter=*beta*',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'v1.31-beta1' })
+
+t.create('no versions matched')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&filter=foobar',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, mockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'no matching versions found' })
+
t.create('invalid maven-metadata.xml uri')
.get(
- '/v.json?metadataUrl=https://repo1.maven.org/maven2/com/google/code/gson/gson/foobar.xml'
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/com/google/code/gson/gson/foobar.xml',
)
.expectBadge({ label: 'maven', message: 'not found' })
+
+t.create('filter with latest strategy')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=latestProperty&filter=*beta*',
+ )
+ .expectBadge({
+ label: 'maven',
+ message: 'filter is not valid with strategy latestProperty',
+ })
+
+t.create('filter with release strategy')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=releaseProperty&filter=*beta*',
+ )
+ .expectBadge({
+ label: 'maven',
+ message: 'filter is not valid with strategy releaseProperty',
+ })
+
+const emptyMockMetaData = `
+
+ mocked-group-id
+ mocked-artifact-id
+
+ 20190902002617
+
+
+`
+
+t.create('release not found')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=releaseProperty',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, emptyMockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: "property 'release' not found" })
+
+t.create('latest not found')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=latestProperty',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, emptyMockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: "property 'latest' not found" })
+
+t.create('no versions')
+ .get(
+ '/v.json?metadataUrl=https://repo1.maven.org/maven2/mocked-group-id/mocked-artifact-id/maven-metadata.xml&strategy=highestVersion',
+ )
+ .intercept(nock =>
+ nock('https://repo1.maven.org/maven2')
+ .get('/mocked-group-id/mocked-artifact-id/maven-metadata.xml')
+ .reply(200, emptyMockMetaData),
+ )
+ .expectBadge({ label: 'maven', message: 'no versions found' })
diff --git a/services/mbin/mbin.service.js b/services/mbin/mbin.service.js
new file mode 100644
index 0000000000000..46c9bc7f71afc
--- /dev/null
+++ b/services/mbin/mbin.service.js
@@ -0,0 +1,71 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { BaseJsonService, InvalidParameter, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ subscriptionsCount: Joi.number().required(),
+}).required()
+
+export default class Mbin extends BaseJsonService {
+ static category = 'social'
+
+ static route = {
+ base: 'mbin',
+ pattern: ':magazine',
+ }
+
+ static openApi = {
+ '/mbin/{magazine}': {
+ get: {
+ summary: 'Mbin',
+ description:
+ 'Mbin is a fork of Kbin, a content aggregator for the Fediverse.',
+ parameters: pathParams({
+ name: 'magazine',
+ description:
+ 'The magazine to query. This is CASE SENSITIVE. Use URL encoding for special characters.',
+ example: 'kbinEarth@kbin.earth',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'magazine', namedLogo: 'activitypub' }
+
+ static render({ magazine, members }) {
+ return {
+ label: `subscribe to ${magazine}`,
+ message: metric(members),
+ style: 'social',
+ color: 'brightgreen',
+ }
+ }
+
+ async fetch({ magazine }) {
+ const splitAlias = magazine.split('@')
+ // The magazine will be in the format of 'magazine@server'
+ if (splitAlias.length !== 2) {
+ throw new InvalidParameter({
+ prettyMessage: 'invalid magazine',
+ })
+ }
+
+ const mag = splitAlias[0]
+ const host = splitAlias[1]
+
+ const data = await this._requestJson({
+ url: `https://${host}/api/magazine/name/${mag}`,
+ schema,
+ httpErrors: {
+ 404: 'magazine not found',
+ },
+ })
+
+ return data.subscriptionsCount
+ }
+
+ async handle({ magazine }) {
+ const members = await this.fetch({ magazine })
+ return this.constructor.render({ magazine, members })
+ }
+}
diff --git a/services/mbin/mbin.tester.js b/services/mbin/mbin.tester.js
new file mode 100644
index 0000000000000..86547fee3271c
--- /dev/null
+++ b/services/mbin/mbin.tester.js
@@ -0,0 +1,43 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('get magazine subscribers')
+ .get('/magazine@instance.tld.json')
+ .intercept(nock =>
+ nock('https://instance.tld/')
+ .get('/api/magazine/name/magazine')
+ .reply(
+ 200,
+ JSON.stringify({
+ subscriptionsCount: 42,
+ }),
+ ),
+ )
+ .expectBadge({
+ label: 'subscribe to magazine@instance.tld',
+ message: '42',
+ color: 'brightgreen',
+ })
+
+t.create('unknown community')
+ .get('/01J12N2ETYG3W5B6G8Y11F5EXG@yups.io.json')
+ .expectBadge({
+ label: 'magazine',
+ message: 'magazine not found',
+ color: 'red',
+ })
+
+t.create('invalid magazine').get('/magazine.invalid.json').expectBadge({
+ label: 'magazine',
+ message: 'invalid magazine',
+ color: 'red',
+})
+
+t.create('test on real mbin magazine for API compliance')
+ .get('/kbinEarth@kbin.earth.json')
+ .expectBadge({
+ label: 'subscribe to kbinEarth@kbin.earth',
+ message: isMetric,
+ color: 'brightgreen',
+ })
diff --git a/services/microbadger/microbadger-base.js b/services/microbadger/microbadger-base.js
deleted file mode 100644
index 949cd594447e5..0000000000000
--- a/services/microbadger/microbadger-base.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
-
-const schema = Joi.object({
- LayerCount: nonNegativeInteger,
- // DownloadSize may be missing in some cases
- DownloadSize: Joi.number()
- .integer()
- .min(0),
- Versions: Joi.array()
- .items(
- Joi.object({
- Tags: Joi.array()
- .items(
- Joi.object({
- tag: Joi.string().required(),
- })
- )
- .required(),
- LayerCount: nonNegativeInteger,
- DownloadSize: Joi.number()
- .integer()
- .min(0),
- })
- )
- .required(),
-}).required()
-
-module.exports = class BaseMicrobadgerService extends BaseJsonService {
- static get category() {
- return 'size'
- }
-
- async fetch({ user, repo }) {
- if (user === '_') {
- user = 'library'
- }
- return this._requestJson({
- schema,
- url: `https://api.microbadger.com/v1/images/${user}/${repo}`,
- })
- }
-
- static getImage(response, tag) {
- if (!tag) {
- return response
- }
- const image =
- response.Versions &&
- response.Versions.find(v => v.Tags.some(t => t.tag === tag))
- if (!image) {
- throw new NotFound()
- }
- return image
- }
-}
diff --git a/services/microbadger/microbadger-layers.service.js b/services/microbadger/microbadger-layers.service.js
deleted file mode 100644
index 5e0549dae3bf4..0000000000000
--- a/services/microbadger/microbadger-layers.service.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict'
-
-const BaseMicrobadgerService = require('./microbadger-base')
-
-module.exports = class MicrobadgerLayers extends BaseMicrobadgerService {
- static get route() {
- return {
- base: 'microbadger/layers',
- pattern: ':user/:repo/:tag*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'MicroBadger Layers',
- pattern: ':user/:repo',
- namedParams: { user: '_', repo: 'alpine' },
- staticPreview: this.render({ layers: 15 }),
- keywords: ['docker'],
- },
- {
- title: 'MicroBadger Layers (tag)',
- pattern: ':user/:repo/:tag',
- namedParams: { user: '_', repo: 'alpine', tag: '2.7' },
- staticPreview: this.render({ layers: 12 }),
- keywords: ['docker'],
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'layers' }
- }
-
- static render({ layers }) {
- return {
- message: layers,
- color: 'blue',
- }
- }
-
- async handle({ user, repo, tag }) {
- const data = await this.fetch({ user, repo })
- const image = this.constructor.getImage(data, tag)
- return this.constructor.render({ layers: image.LayerCount })
- }
-}
diff --git a/services/microbadger/microbadger-layers.tester.js b/services/microbadger/microbadger-layers.tester.js
deleted file mode 100644
index d2fd1a828babb..0000000000000
--- a/services/microbadger/microbadger-layers.tester.js
+++ /dev/null
@@ -1,41 +0,0 @@
-'use strict'
-
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('layers without a specified tag')
- .get('/_/alpine.json')
- .timeout(150000)
- .expectBadge({
- label: 'layers',
- message: nonNegativeInteger,
- })
-
-t.create('layers with a specified tag')
- .get('/_/alpine/2.7.json')
- .timeout(150000)
- .expectBadge({
- label: 'layers',
- message: nonNegativeInteger,
- })
-
-t.create('specified tag when repository has only one')
- .get('/_/alpine/wrong-tag.json')
- .expectBadge({ label: 'layers', message: 'not found' })
-
-t.create('nonexistent repository')
- .get('/_/not-a-real-repo.json')
- .expectBadge({ label: 'layers', message: 'not found' })
-
-t.create('nonexistent tag')
- .get('/_/unknown/wrong-tag.json')
- .intercept(nock =>
- nock('https://api.microbadger.com')
- .get('/v1/images/library/unknown')
- .reply(200, {
- LayerCount: 1,
- DownloadSize: 1,
- Versions: [],
- })
- )
- .expectBadge({ label: 'layers', message: 'not found' })
diff --git a/services/microbadger/microbadger-size.service.js b/services/microbadger/microbadger-size.service.js
deleted file mode 100644
index bfc6bd2e723d8..0000000000000
--- a/services/microbadger/microbadger-size.service.js
+++ /dev/null
@@ -1,78 +0,0 @@
-'use strict'
-
-const prettyBytes = require('pretty-bytes')
-const { NotFound } = require('..')
-const BaseMicrobadgerService = require('./microbadger-base')
-
-const documentation = `
-
- The MicroBadger API can sometimes be a bit temperamental when it comes to retrieving the size of your image.
- If the website indicates "Layer information not yet retrieved" for your image, Shields.io will display the size as unknown in its badge.
- In some cases, the size is also reported as 0.
-
-
- To speed things up on the MicroBadger side of things, you may want to hit their webhook manually.
- Simply go to your image's page on the MicroBadger website, click "Get the webhook" and follow the instructions listed there.
- Please be patient, it may still take several minutes for the information to be made available to Shields.io!
-
-
- Feel free to open an issue if you're still facing issues, but you may want to have a glance at some of these beforehand:
-
- https://github.com/badges/shields/issues/3988
- https://github.com/badges/shields/issues/2532
- https://github.com/badges/shields/pull/1471#discussion_r165825062
- https://github.com/microscaling/microbadger/issues/38
- https://github.com/microscaling/microbadger/issues/47
-
-
-`
-
-module.exports = class MicrobadgerSize extends BaseMicrobadgerService {
- static get route() {
- return {
- base: 'microbadger/image-size',
- pattern: ':user/:repo/:tag*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'MicroBadger Size',
- pattern: ':user/:repo',
- namedParams: { user: 'fedora', repo: 'apache' },
- staticPreview: this.render({ size: 126000000 }),
- keywords: ['docker'],
- documentation,
- },
- {
- title: 'MicroBadger Size (tag)',
- pattern: ':user/:repo/:tag',
- namedParams: { user: 'fedora', repo: 'apache', tag: 'latest' },
- staticPreview: this.render({ size: 103000000 }),
- keywords: ['docker'],
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'image size' }
- }
-
- static render({ size }) {
- return {
- message: prettyBytes(parseInt(size)),
- color: 'blue',
- }
- }
-
- async handle({ user, repo, tag }) {
- const data = await this.fetch({ user, repo })
- const image = this.constructor.getImage(data, tag)
- if (image.DownloadSize === undefined) {
- throw new NotFound({ prettyMessage: 'unknown' })
- }
- return this.constructor.render({ size: image.DownloadSize })
- }
-}
diff --git a/services/microbadger/microbadger-size.tester.js b/services/microbadger/microbadger-size.tester.js
deleted file mode 100644
index 4e6613e6e7f0f..0000000000000
--- a/services/microbadger/microbadger-size.tester.js
+++ /dev/null
@@ -1,53 +0,0 @@
-'use strict'
-
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('image size without a specified tag')
- .get('/fedora/apache.json')
- .timeout(150000)
- .expectBadge({
- label: 'image size',
- message: isFileSize,
- })
-
-t.create('image size with a specified tag')
- .get('/fedora/apache/latest.json')
- .timeout(150000)
- .expectBadge({
- label: 'image size',
- message: isFileSize,
- })
-
-t.create('missing download size')
- .get('/puppet/puppetserver.json')
- .intercept(nock =>
- nock('https://api.microbadger.com')
- .get('/v1/images/puppet/puppetserver')
- .reply(200, {
- LayerCount: 1,
- Versions: [],
- })
- )
- .expectBadge({ label: 'image size', message: 'unknown' })
-
-t.create('specified tag when repository has only one')
- .get('/_/alpine/wrong-tag.json')
- .expectBadge({ label: 'image size', message: 'not found' })
-
-t.create('nonexistent repository')
- .get('/_/not-a-real-repo.json')
- .expectBadge({ label: 'image size', message: 'not found' })
-
-t.create('nonexistent tag')
- .get('/_/unknown/wrong-tag.json')
- .intercept(nock =>
- nock('https://api.microbadger.com')
- .get('/v1/images/library/unknown')
- .reply(200, {
- LayerCount: 1,
- DownloadSize: 1,
- Versions: [],
- })
- )
- .expectBadge({ label: 'image size', message: 'not found' })
diff --git a/services/modrinth/modrinth-base.js b/services/modrinth/modrinth-base.js
new file mode 100644
index 0000000000000..1afc68eaa0bd0
--- /dev/null
+++ b/services/modrinth/modrinth-base.js
@@ -0,0 +1,38 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+
+const projectSchema = Joi.object({
+ downloads: nonNegativeInteger,
+ followers: nonNegativeInteger,
+}).required()
+
+const versionSchema = Joi.array()
+ .items(
+ Joi.object({
+ version_number: Joi.string().required(),
+ game_versions: Joi.array().items(Joi.string()).min(1).required(),
+ }).required(),
+ )
+ .required()
+
+const description =
+ "You can use your project slug, or the project ID. The ID can be found in the 'Technical information' section of your Modrinth page.
"
+
+class BaseModrinthService extends BaseJsonService {
+ async fetchVersions({ projectId }) {
+ return this._requestJson({
+ schema: versionSchema,
+ url: `https://api.modrinth.com/v2/project/${projectId}/version`,
+ })
+ }
+
+ async fetchProject({ projectId }) {
+ return this._requestJson({
+ schema: projectSchema,
+ url: `https://api.modrinth.com/v2/project/${projectId}`,
+ })
+ }
+}
+
+export { BaseModrinthService, description }
diff --git a/services/modrinth/modrinth-downloads.service.js b/services/modrinth/modrinth-downloads.service.js
new file mode 100644
index 0000000000000..ca8e1d8324d1f
--- /dev/null
+++ b/services/modrinth/modrinth-downloads.service.js
@@ -0,0 +1,32 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseModrinthService, description } from './modrinth-base.js'
+
+export default class ModrinthDownloads extends BaseModrinthService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'modrinth/dt',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/modrinth/dt/{projectId}': {
+ get: {
+ summary: 'Modrinth Downloads',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'AANobbMI',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ projectId }) {
+ const { downloads } = await this.fetchProject({ projectId })
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/modrinth/modrinth-downloads.tester.js b/services/modrinth/modrinth-downloads.tester.js
new file mode 100644
index 0000000000000..26ac709ab67fd
--- /dev/null
+++ b/services/modrinth/modrinth-downloads.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Downloads')
+ .get('/AANobbMI.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('Downloads (not found)')
+ .get('/not-existing.json')
+ .expectBadge({ label: 'downloads', message: 'not found', color: 'red' })
diff --git a/services/modrinth/modrinth-followers.service.js b/services/modrinth/modrinth-followers.service.js
new file mode 100644
index 0000000000000..b845768f8f3ab
--- /dev/null
+++ b/services/modrinth/modrinth-followers.service.js
@@ -0,0 +1,40 @@
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseModrinthService, description } from './modrinth-base.js'
+
+export default class ModrinthFollowers extends BaseModrinthService {
+ static category = 'social'
+
+ static route = {
+ base: 'modrinth/followers',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/modrinth/followers/{projectId}': {
+ get: {
+ summary: 'Modrinth Followers',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'AANobbMI',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'followers', namedLogo: 'modrinth' }
+
+ static render({ followers }) {
+ return {
+ message: metric(followers),
+ style: 'social',
+ color: 'blue',
+ }
+ }
+
+ async handle({ projectId }) {
+ const { followers } = await this.fetchProject({ projectId })
+ return this.constructor.render({ followers })
+ }
+}
diff --git a/services/modrinth/modrinth-followers.tester.js b/services/modrinth/modrinth-followers.tester.js
new file mode 100644
index 0000000000000..2e39bc464ecf9
--- /dev/null
+++ b/services/modrinth/modrinth-followers.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Followers')
+ .get('/AANobbMI.json')
+ .expectBadge({ label: 'followers', message: isMetric })
+
+t.create('Followers (not found)')
+ .get('/not-existing.json')
+ .expectBadge({ label: 'followers', message: 'not found', color: 'red' })
diff --git a/services/modrinth/modrinth-game-versions.service.js b/services/modrinth/modrinth-game-versions.service.js
new file mode 100644
index 0000000000000..d278a050d077c
--- /dev/null
+++ b/services/modrinth/modrinth-game-versions.service.js
@@ -0,0 +1,45 @@
+import { pathParams } from '../index.js'
+import { BaseModrinthService, description } from './modrinth-base.js'
+
+export default class ModrinthGameVersions extends BaseModrinthService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'modrinth/game-versions',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/modrinth/game-versions/{projectId}': {
+ get: {
+ summary: 'Modrinth Game Versions',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'AANobbMI',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'game versions' }
+
+ static render({ versions }) {
+ if (versions.length > 5) {
+ return {
+ message: `${versions[0]} | ${versions[1]} | ... | ${versions[versions.length - 2]} | ${versions[versions.length - 1]}`,
+ color: 'blue',
+ }
+ }
+ return {
+ message: versions.join(' | '),
+ color: 'blue',
+ }
+ }
+
+ async handle({ projectId }) {
+ const { 0: latest } = await this.fetchVersions({ projectId })
+ const versions = latest.game_versions
+ return this.constructor.render({ versions })
+ }
+}
diff --git a/services/modrinth/modrinth-game-versions.spec.js b/services/modrinth/modrinth-game-versions.spec.js
new file mode 100644
index 0000000000000..bf2b781578688
--- /dev/null
+++ b/services/modrinth/modrinth-game-versions.spec.js
@@ -0,0 +1,22 @@
+import { test, given } from 'sazerac'
+import ModrinthGameVersions from './modrinth-game-versions.service.js'
+
+describe('render function', function () {
+ it('displays up to five versions', async function () {
+ test(ModrinthGameVersions.render, () => {
+ given({ versions: ['1.1', '1.2', '1.3', '1.4', '1.5'] }).expect({
+ message: '1.1 | 1.2 | 1.3 | 1.4 | 1.5',
+ color: 'blue',
+ })
+ })
+ })
+
+ it('uses ellipsis for six versions or more', async function () {
+ test(ModrinthGameVersions.render, () => {
+ given({ versions: ['1.1', '1.2', '1.3', '1.4', '1.5', '1.6'] }).expect({
+ message: '1.1 | 1.2 | ... | 1.5 | 1.6',
+ color: 'blue',
+ })
+ })
+ })
+})
diff --git a/services/modrinth/modrinth-game-versions.tester.js b/services/modrinth/modrinth-game-versions.tester.js
new file mode 100644
index 0000000000000..4820c8a3b371d
--- /dev/null
+++ b/services/modrinth/modrinth-game-versions.tester.js
@@ -0,0 +1,21 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Game Versions')
+ .get('/AANobbMI.json')
+ .expectBadge({
+ label: 'game versions',
+ message: Joi.alternatives().try(
+ withRegex(/^(\d+\.\d+(\.\d+)?( \| )?)+$/),
+ withRegex(
+ /^\d+\.\d+(\.\d+)? \| \d+\.\d+(\.\d+)? \| \.\.\. \| \d+\.\d+(\.\d+)? \| \d+\.\d+(\.\d+)?$/,
+ ),
+ ),
+ })
+
+t.create('Game Versions (not found)')
+ .get('/not-existing.json')
+ .expectBadge({ label: 'game versions', message: 'not found', color: 'red' })
diff --git a/services/modrinth/modrinth-version.service.js b/services/modrinth/modrinth-version.service.js
new file mode 100644
index 0000000000000..93e351f8b7a9f
--- /dev/null
+++ b/services/modrinth/modrinth-version.service.js
@@ -0,0 +1,33 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseModrinthService, description } from './modrinth-base.js'
+
+export default class ModrinthVersion extends BaseModrinthService {
+ static category = 'version'
+
+ static route = {
+ base: 'modrinth/v',
+ pattern: ':projectId',
+ }
+
+ static openApi = {
+ '/modrinth/v/{projectId}': {
+ get: {
+ summary: 'Modrinth Version',
+ description,
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'AANobbMI',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'version' }
+
+ async handle({ projectId }) {
+ const { 0: latest } = await this.fetchVersions({ projectId })
+ const version = latest.version_number
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/modrinth/modrinth-version.tester.js b/services/modrinth/modrinth-version.tester.js
new file mode 100644
index 0000000000000..58366755d0642
--- /dev/null
+++ b/services/modrinth/modrinth-version.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Version')
+ .get('/AANobbMI.json')
+ .expectBadge({ label: 'version', message: withRegex(/.*\d+\.\d+(\.d+)?.*/) })
+
+t.create('Version (not found)')
+ .get('/not-existing.json')
+ .expectBadge({ label: 'version', message: 'not found', color: 'red' })
diff --git a/services/mozilla-observatory/mozilla-observatory.service.js b/services/mozilla-observatory/mozilla-observatory.service.js
index 80e0d5cacdb9d..a0d47d4d839d1 100644
--- a/services/mozilla-observatory/mozilla-observatory.service.js
+++ b/services/mozilla-observatory/mozilla-observatory.service.js
@@ -1,99 +1,54 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, pathParam } from '../index.js'
const schema = Joi.object({
- state: Joi.string()
- .valid('ABORTED', 'FAILED', 'FINISHED', 'PENDING', 'STARTING', 'RUNNING')
- .required(),
- grade: Joi.alternatives()
- .conditional('state', {
- is: 'FINISHED',
- then: Joi.string().regex(/^[ABCDEF][+-]?$/),
- otherwise: Joi.valid(null),
- })
+ grade: Joi.string()
+ .regex(/^[ABCDEF][+-]?$/)
.required(),
- score: Joi.alternatives()
- .conditional('state', {
- is: 'FINISHED',
- then: Joi.number()
- .integer()
- .min(0)
- .max(200),
- otherwise: Joi.valid(null),
- })
- .required(),
-}).required()
-
-const queryParamSchema = Joi.object({
- publish: Joi.equal(''),
+ score: Joi.number().integer().min(0).max(200).required(),
}).required()
-const documentation = `
-
- The Mozilla HTTP Observatory
- is a set of tools to analyze your website
- and inform you if you are utilizing the many available methods to secure it.
-
-
- By default the scan result is hidden from the public result list.
- You can activate the publication of the scan result
- by setting the publish parameter.
-
-
- The badge returns a cached site result if the site has been scanned anytime in the previous 24 hours.
- If you need to force invalidating the cache,
- you can to do it manually through the Mozilla Observatory Website
-
+const description = `
+The [Mozilla HTTP Observatory](https://developer.mozilla.org/en-US/observatory)
+is a set of security tools to analyze your website
+and inform you if you are utilizing the many available methods to secure it.
`
-module.exports = class MozillaObservatory extends BaseJsonService {
- static get category() {
- // TODO: Once created, change to a more appropriate category,
- // see https://github.com/badges/shields/pull/2926#issuecomment-460777017
- return 'monitoring'
- }
+export default class MozillaObservatory extends BaseJsonService {
+ // TODO: Once created, change to a more appropriate category,
+ // see https://github.com/badges/shields/pull/2926#issuecomment-460777017
+ static category = 'monitoring'
- static get route() {
- return {
- base: 'mozilla-observatory',
- pattern: ':format(grade|grade-score)/:host',
- queryParamSchema,
- }
+ static route = {
+ base: 'mozilla-observatory',
+ pattern: ':format(grade|grade-score)/:host',
}
- static get examples() {
- return [
- {
- title: 'Mozilla HTTP Observatory Grade',
- namedParams: { format: 'grade', host: 'github.com' },
- staticPreview: this.render({
- format: 'grade',
- state: 'FINISHED',
- grade: 'A+',
- score: 115,
- }),
- queryParams: { publish: null },
- keywords: ['scanner', 'security'],
- documentation,
+ static openApi = {
+ '/mozilla-observatory/{format}/{host}': {
+ get: {
+ summary: 'Mozilla HTTP Observatory Grade',
+ description,
+ parameters: [
+ pathParam({
+ name: 'format',
+ example: 'grade',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ }),
+ pathParam({
+ name: 'host',
+ example: 'github.com',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'observatory',
- }
+ static defaultBadgeData = {
+ label: 'observatory',
}
- static render({ format, state, grade, score }) {
- if (state !== 'FINISHED') {
- return {
- message: state.toLowerCase(),
- color: 'lightgrey',
- }
- }
+ static render({ format, grade, score }) {
const letter = grade[0].toLowerCase()
const colorMap = {
a: 'brightgreen',
@@ -109,20 +64,23 @@ module.exports = class MozillaObservatory extends BaseJsonService {
}
}
- async fetch({ host, publish }) {
+ async fetch({ host }) {
return this._requestJson({
schema,
- url: `https://http-observatory.security.mozilla.org/api/v1/analyze`,
+ url: 'https://observatory-api.mdn.mozilla.net/api/v2/scan',
options: {
method: 'POST',
- qs: { host },
- form: { hidden: !publish },
+ searchParams: { host },
},
})
}
- async handle({ format, host }, { publish }) {
- const { state, grade, score } = await this.fetch({ host, publish })
- return this.constructor.render({ format, state, grade, score })
+ async handle({ format, host }) {
+ const scan = await this.fetch({ host })
+ return this.constructor.render({
+ format,
+ grade: scan.grade,
+ score: scan.score,
+ })
}
}
diff --git a/services/mozilla-observatory/mozilla-observatory.spec.js b/services/mozilla-observatory/mozilla-observatory.spec.js
new file mode 100644
index 0000000000000..a2e73c40bbb9b
--- /dev/null
+++ b/services/mozilla-observatory/mozilla-observatory.spec.js
@@ -0,0 +1,93 @@
+import { test, given } from 'sazerac'
+import MozillaObservatory from './mozilla-observatory.service.js'
+
+describe('MozillaObservatory', function () {
+ test(MozillaObservatory.render, () => {
+ given({ format: 'grade', grade: 'A' }).expect({
+ message: 'A',
+ color: 'brightgreen',
+ })
+ given({ format: 'grade', grade: 'A+' }).expect({
+ message: 'A+',
+ color: 'brightgreen',
+ })
+ given({ format: 'grade', grade: 'A-' }).expect({
+ message: 'A-',
+ color: 'brightgreen',
+ })
+
+ given({ format: 'grade', grade: 'B' }).expect({
+ message: 'B',
+ color: 'green',
+ })
+ given({ format: 'grade', grade: 'B+' }).expect({
+ message: 'B+',
+ color: 'green',
+ })
+ given({ format: 'grade', grade: 'B-' }).expect({
+ message: 'B-',
+ color: 'green',
+ })
+
+ given({ format: 'grade', grade: 'C' }).expect({
+ message: 'C',
+ color: 'yellow',
+ })
+ given({ format: 'grade', grade: 'C+' }).expect({
+ message: 'C+',
+ color: 'yellow',
+ })
+ given({ format: 'grade', grade: 'C-' }).expect({
+ message: 'C-',
+ color: 'yellow',
+ })
+
+ given({ format: 'grade', grade: 'D' }).expect({
+ message: 'D',
+ color: 'orange',
+ })
+ given({ format: 'grade', grade: 'D+' }).expect({
+ message: 'D+',
+ color: 'orange',
+ })
+ given({ format: 'grade', grade: 'D-' }).expect({
+ message: 'D-',
+ color: 'orange',
+ })
+
+ given({ format: 'grade', grade: 'E' }).expect({
+ message: 'E',
+ color: 'orange',
+ })
+ given({ format: 'grade', grade: 'E+' }).expect({
+ message: 'E+',
+ color: 'orange',
+ })
+ given({ format: 'grade', grade: 'E-' }).expect({
+ message: 'E-',
+ color: 'orange',
+ })
+
+ given({ format: 'grade', grade: 'F' }).expect({
+ message: 'F',
+ color: 'red',
+ })
+ given({ format: 'grade', grade: 'F+' }).expect({
+ message: 'F+',
+ color: 'red',
+ })
+ given({ format: 'grade', grade: 'F-' }).expect({
+ message: 'F-',
+ color: 'red',
+ })
+
+ given({
+ format: 'grade-score',
+ grade: 'A',
+ score: '115',
+ }).expect({
+ message: 'A (115/100)',
+ color: 'brightgreen',
+ })
+ })
+})
diff --git a/services/mozilla-observatory/mozilla-observatory.tester.js b/services/mozilla-observatory/mozilla-observatory.tester.js
index d9e6febbd0a43..92fd1cd103b4e 100644
--- a/services/mozilla-observatory/mozilla-observatory.tester.js
+++ b/services/mozilla-observatory/mozilla-observatory.tester.js
@@ -1,326 +1,19 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isMessage = Joi.alternatives()
- .try(
- Joi.string().regex(/^[ABCDEF][+-]? \([0-9]{1,3}\/100\)$/),
- Joi.string().allow('pending')
- )
+ .try(Joi.string().regex(/^[ABCDEF][+-]? \([0-9]{1,3}\/100\)$/))
.required()
-t.create('request on observatory.mozilla.org')
- .timeout(10000)
- .get('/grade-score/observatory.mozilla.org.json')
- .expectBadge({
- label: 'observatory',
- message: isMessage,
- })
-
-t.create('request on observatory.mozilla.org with inclusion in public results')
- .timeout(10000)
- .get('/grade-score/observatory.mozilla.org.json?publish')
- .expectBadge({
- label: 'observatory',
- message: isMessage,
- })
-
-t.create('grade without score (mock)')
- .get('/grade/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'A', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'A',
- color: 'brightgreen',
- })
-
-t.create('grade A with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'A', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'A (115/100)',
- color: 'brightgreen',
- })
-
-t.create('grade A+ with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'A+', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'A+ (115/100)',
- color: 'brightgreen',
- })
-
-t.create('grade A- with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'A-', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'A- (115/100)',
- color: 'brightgreen',
- })
-
-t.create('grade B with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'B', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'B (115/100)',
- color: 'green',
- })
-
-t.create('grade B+ with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'B+', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'B+ (115/100)',
- color: 'green',
- })
-
-t.create('grade B- with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'B-', score: 115 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'B- (115/100)',
- color: 'green',
- })
-
-t.create('grade C with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'C', score: 80 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'C (80/100)',
- color: 'yellow',
- })
-
-t.create('grade C+ with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'C+', score: 80 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'C+ (80/100)',
- color: 'yellow',
- })
-
-t.create('grade C- with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'C-', score: 80 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'C- (80/100)',
- color: 'yellow',
- })
-
-t.create('grade D with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'D', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'D (15/100)',
- color: 'orange',
- })
-
-t.create('grade D+ with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'D+', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'D+ (15/100)',
- color: 'orange',
- })
-
-t.create('grade D- with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'D-', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'D- (15/100)',
- color: 'orange',
- })
-
-t.create('grade E with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'E', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'E (15/100)',
- color: 'orange',
- })
-
-t.create('grade E+ with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'E+', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'E+ (15/100)',
- color: 'orange',
- })
-
-t.create('grade E- with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'E-', score: 15 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'E- (15/100)',
- color: 'orange',
- })
-
-t.create('grade F with score (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FINISHED', grade: 'F', score: 0 })
- )
- .expectBadge({
- label: 'observatory',
- message: 'F (0/100)',
- color: 'red',
- })
-
-t.create('aborted (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'ABORTED', grade: null, score: null })
- )
- .expectBadge({
- label: 'observatory',
- message: 'aborted',
- color: 'lightgrey',
- })
-
-t.create('failed (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'FAILED', grade: null, score: null })
- )
- .expectBadge({
- label: 'observatory',
- message: 'failed',
- color: 'lightgrey',
- })
-
-t.create('pending (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'PENDING', grade: null, score: null })
- )
- .expectBadge({
- label: 'observatory',
- message: 'pending',
- color: 'lightgrey',
- })
-
-t.create('starting (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'STARTING', grade: null, score: null })
- )
- .expectBadge({
- label: 'observatory',
- message: 'starting',
- color: 'lightgrey',
- })
-
-t.create('running (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'RUNNING', grade: null, score: null })
- )
- .expectBadge({
- label: 'observatory',
- message: 'running',
- color: 'lightgrey',
- })
+t.create('valid').get('/grade-score/observatory.mozilla.org.json').expectBadge({
+ label: 'observatory',
+ message: isMessage,
+})
-t.create('invalid response with grade and score but not finished (mock)')
- .get('/grade-score/foo.bar.json')
- .intercept(nock =>
- nock('https://http-observatory.security.mozilla.org')
- .post('/api/v1/analyze?host=foo.bar')
- .reply(200, { state: 'RUNNING', grade: 'A+', score: 135 })
- )
+t.create('invalid')
+ .get('/grade-score/invalidsubdomain.shields.io.json')
.expectBadge({
label: 'observatory',
- message: 'invalid response data',
- color: 'lightgrey',
+ message: 'invalid',
})
diff --git a/services/myget/myget.service.js b/services/myget/myget.service.js
index f0adfcf92cca0..f3e8ebfd340a0 100644
--- a/services/myget/myget.service.js
+++ b/services/myget/myget.service.js
@@ -1,69 +1,81 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { createServiceFamily } from '../nuget/nuget-v3-service-family.js'
-const { createServiceFamily } = require('../nuget/nuget-v3-service-family')
-
-const {
- NugetVersionService: Version,
- NugetDownloadService: Downloads,
-} = createServiceFamily({
- defaultLabel: 'myget',
- serviceBaseUrl: 'myget',
- apiDomain: 'myget.org',
-})
+const { NugetVersionService: Version, NugetDownloadService: Downloads } =
+ createServiceFamily({
+ defaultLabel: 'myget',
+ serviceBaseUrl: 'myget',
+ apiDomain: 'myget.org',
+ })
class MyGetVersionService extends Version {
- static get examples() {
- return [
- {
- title: 'MyGet',
- pattern: 'myget/:feed/v/:packageName',
- namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
- staticPreview: this.render({ version: '2.6.1' }),
- },
- {
- title: 'MyGet (with prereleases)',
- pattern: 'myget/:feed/vpre/:packageName',
- namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
- staticPreview: this.render({ version: '2.7.0-beta0001' }),
+ static openApi = {
+ '/myget/{feed}/{variant}/{packageName}': {
+ get: {
+ summary: 'MyGet Version',
+ parameters: pathParams(
+ { name: 'feed', example: 'mongodb' },
+ {
+ name: 'variant',
+ example: 'v',
+ schema: { type: 'variant', enum: ['v', 'vpre'] },
+ description:
+ 'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
+ },
+ { name: 'packageName', example: 'MongoDB.Driver.Core' },
+ ),
},
- {
- title: 'MyGet tenant',
- pattern: ':tenant.myget/:feed/v/:packageName',
- namedParams: {
- tenant: 'dotnet',
- feed: 'dotnet-coreclr',
- packageName: 'Microsoft.DotNet.CoreCLR',
- },
- staticPreview: this.render({ version: '1.0.2-prerelease' }),
+ },
+ '/{tenant}/{feed}/{variant}/{packageName}': {
+ get: {
+ summary: 'MyGet Version (tenant)',
+ parameters: pathParams(
+ {
+ name: 'tenant',
+ example: 'vs-devcore.myget',
+ description: 'MyGet Tenant in the format `name.myget`',
+ },
+ { name: 'feed', example: 'vs-devcore' },
+ {
+ name: 'variant',
+ example: 'v',
+ schema: { type: 'variant', enum: ['v', 'vpre'] },
+ description:
+ 'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
+ },
+ { name: 'packageName', example: 'MicroBuild' },
+ ),
},
- ]
+ },
}
}
class MyGetDownloadService extends Downloads {
- static get examples() {
- return [
- {
- title: 'MyGet',
- pattern: 'myget/:feed/dt/:packageName',
- namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
- staticPreview: this.render({ downloads: 419 }),
+ static openApi = {
+ '/myget/{feed}/dt/{packageName}': {
+ get: {
+ summary: 'MyGet Downloads',
+ parameters: pathParams(
+ { name: 'feed', example: 'mongodb' },
+ { name: 'packageName', example: 'MongoDB.Driver.Core' },
+ ),
},
- {
- title: 'MyGet tenant',
- pattern: ':tenant.myget/:feed/dt/:packageName',
- namedParams: {
- tenant: 'dotnet',
- feed: 'dotnet-coreclr',
- packageName: 'Microsoft.DotNet.CoreCLR',
- },
- staticPreview: this.render({ downloads: 9748 }),
+ },
+ '/{tenant}/{feed}/dt/{packageName}': {
+ get: {
+ summary: 'MyGet Downloads (tenant)',
+ parameters: pathParams(
+ {
+ name: 'tenant',
+ example: 'vs-devcore.myget',
+ description: 'MyGet Tenant in the format `name.myget`',
+ },
+ { name: 'feed', example: 'vs-devcore' },
+ { name: 'packageName', example: 'MicroBuild' },
+ ),
},
- ]
+ },
}
}
-module.exports = {
- MyGetVersionService,
- MyGetDownloadService,
-}
+export { MyGetVersionService, MyGetDownloadService }
diff --git a/services/myget/myget.tester.js b/services/myget/myget.tester.js
index 525e32c48dea3..812f7247cd7d3 100644
--- a/services/myget/myget.tester.js
+++ b/services/myget/myget.tester.js
@@ -1,23 +1,15 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const {
+import { ServiceTester } from '../tester.js'
+import {
isMetric,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const {
- queryIndex,
- nuGetV3VersionJsonWithDash,
- nuGetV3VersionJsonFirstCharZero,
- nuGetV3VersionJsonFirstCharNotZero,
-} = require('../nuget-fixtures')
-const { invalidJSON } = require('../response-fixtures')
+} from '../test-validators.js'
+import { invalidJSON } from '../response-fixtures.js'
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'myget',
title: 'MyGet',
pathPrefix: '',
-}))
+})
// downloads
@@ -29,7 +21,7 @@ t.create('total downloads (valid)')
})
t.create('total downloads (tenant)')
- .get('/dotnet.myget/dotnet-coreclr/dt/Microsoft.DotNet.CoreCLR.json')
+ .get('/vs-devcore.myget/vs-devcore/dt/MicroBuild.json')
.expectBadge({
label: 'downloads',
message: isMetric,
@@ -39,22 +31,22 @@ t.create('total downloads (not found)')
.get('/myget/mongodb/dt/not-a-real-package.json')
.expectBadge({ label: 'downloads', message: 'package not found' })
-// This tests the erroring behavior in regular-update.
+// This tests the erroring behavior in getCachedResource.
t.create('total downloads (connection error)')
.get('/myget/mongodb/dt/MongoDB.Driver.Core.json')
.networkOff()
.expectBadge({
label: 'downloads',
- message: 'intermediate resource inaccessible',
+ message: 'inaccessible',
})
-// This tests the erroring behavior in regular-update.
+// This tests the erroring behavior in getCachedResource.
t.create('total downloads (unexpected first response)')
.get('/myget/mongodb/dt/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
- .reply(invalidJSON)
+ .reply(invalidJSON),
)
.expectBadge({
label: 'downloads',
@@ -70,73 +62,13 @@ t.create('version (valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
-t.create('total downloads (tenant)')
- .get('/dotnet.myget/dotnet-coreclr/v/Microsoft.DotNet.CoreCLR.json')
+t.create('version (tenant)')
+ .get('/vs-devcore.myget/vs-devcore/v/MicroBuild.json')
.expectBadge({
- label: 'dotnet-coreclr',
+ label: 'vs-devcore',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
-t.create('version (yellow badge)')
- .get('/myget/mongodb/v/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonWithDash)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v1.2-beta',
- color: 'yellow',
- })
-
-t.create('version (orange badge)')
- .get('/myget/mongodb/v/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharZero)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v0.35',
- color: 'orange',
- })
-
-t.create('version (blue badge)')
- .get('/myget/mongodb/v/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharNotZero)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v1.2.7',
- color: 'blue',
- })
-
t.create('version (not found)')
.get('/myget/foo/v/not-a-real-package.json')
.expectBadge({ label: 'myget', message: 'package not found' })
@@ -150,66 +82,6 @@ t.create('version (pre) (valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
-t.create('version (pre) (yellow badge)')
- .get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonWithDash)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v1.2-beta',
- color: 'yellow',
- })
-
-t.create('version (pre) (orange badge)')
- .get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharZero)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v0.35',
- color: 'orange',
- })
-
-t.create('version (pre) (blue badge)')
- .get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
- .intercept(nock =>
- nock('https://www.myget.org')
- .get('/F/mongodb/api/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharNotZero)
- )
- .expectBadge({
- label: 'mongodb',
- message: 'v1.2.7',
- color: 'blue',
- })
-
t.create('version (pre) (not found)')
.get('/myget/foo/vpre/not-a-real-package.json')
.expectBadge({ label: 'myget', message: 'package not found' })
diff --git a/services/netlify/netlify.service.js b/services/netlify/netlify.service.js
index 0d39e00cdecc9..dd8ae105b2c82 100644
--- a/services/netlify/netlify.service.js
+++ b/services/netlify/netlify.service.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const { renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService } = require('..')
+import { renderBuildStatusBadge } from '../build-status.js'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
const pendingStatus = 'building'
const notBuiltStatus = 'not built'
@@ -16,36 +14,30 @@ const statusMap = {
infrastructure_failure: 'failed',
}
-module.exports = class Netlify extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
+export default class Netlify extends BaseSvgScrapingService {
+ static category = 'build'
- static get route() {
- return {
- base: 'netlify',
- pattern: ':projectId',
- }
+ static route = {
+ base: 'netlify',
+ pattern: ':projectId',
}
- static get examples() {
- return [
- {
- title: 'Netlify',
- namedParams: {
- projectId: 'e6d5a4e0-dee1-4261-833e-2f47f509c68f',
- },
- documentation:
+ static openApi = {
+ '/netlify/{projectId}': {
+ get: {
+ summary: 'Netlify',
+ description:
'To locate your project id, visit your project settings, scroll to "Status badges" under "General", and copy the ID between "/api/v1/badges/" and "/deploy-status" in the code sample',
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+ parameters: pathParams({
+ name: 'projectId',
+ example: 'e6d5a4e0-dee1-4261-833e-2f47f509c68f',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'netlify',
- }
+ static defaultBadgeData = {
+ label: 'netlify',
}
static render({ status }) {
@@ -57,19 +49,20 @@ module.exports = class Netlify extends BaseSvgScrapingService {
return result
}
- async fetch({ projectId, branch }) {
+ async fetch({ projectId }) {
const url = `https://api.netlify.com/api/v1/badges/${projectId}/deploy-status`
const { buffer } = await this._request({
url,
})
- if (buffer.includes('#EAFAF9')) return { message: 'passing' }
- if (buffer.includes('#FFF3F4')) return { message: 'failing' }
- if (buffer.includes('#FEFAEA')) return { message: 'building' }
+ if (buffer.includes('#0F4A21')) return { message: 'passing' }
+ if (buffer.includes('#800A20')) return { message: 'failing' }
+ if (buffer.includes('#603408')) return { message: 'building' }
+ if (buffer.includes('#181A1C')) return { message: 'canceled' }
return { message: 'unknown' }
}
- async handle({ projectId, branch }) {
- const { message: status } = await this.fetch({ projectId, branch })
+ async handle({ projectId }) {
+ const { message: status } = await this.fetch({ projectId })
return this.constructor.render({ status })
}
}
diff --git a/services/netlify/netlify.spec.js b/services/netlify/netlify.spec.js
index f35e439cd185d..c614e5ee62e1b 100644
--- a/services/netlify/netlify.spec.js
+++ b/services/netlify/netlify.spec.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const Netlify = require('./netlify.service')
+import { test, given } from 'sazerac'
+import Netlify from './netlify.service.js'
const building = { message: 'building', label: undefined, color: 'yellow' }
const notBuilt = { message: 'not built', label: undefined, color: undefined }
-describe('Netlify', function() {
+describe('Netlify', function () {
test(Netlify.render, () => {
given({ status: 'building' }).expect(building)
given({ status: 'stopped' }).expect(notBuilt)
diff --git a/services/netlify/netlify.tester.js b/services/netlify/netlify.tester.js
index abea04611958a..8d9d8a03eab98 100644
--- a/services/netlify/netlify.tester.js
+++ b/services/netlify/netlify.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('netlify (valid, no branch)')
.get('/e6d5a4e0-dee1-4261-833e-2f47f509c68f.json')
diff --git a/services/nexus/nexus-redirect.service.js b/services/nexus/nexus-redirect.service.js
index fb194284aefa5..12878567219c8 100644
--- a/services/nexus/nexus-redirect.service.js
+++ b/services/nexus/nexus-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
redirector({
category: 'version',
route: {
diff --git a/services/nexus/nexus-redirect.tester.js b/services/nexus/nexus-redirect.tester.js
index 7e6b3f56a86d2..ada3d86dfd967 100644
--- a/services/nexus/nexus-redirect.tester.js
+++ b/services/nexus/nexus-redirect.tester.js
@@ -1,35 +1,33 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'NexusRedirect',
title: 'NexusRedirect',
pathPrefix: '/nexus',
-}))
+})
t.create('Nexus release')
.get('/r/https/oss.sonatype.org/com.google.guava/guava.svg')
.expectRedirect(
`/nexus/r/com.google.guava/guava.svg?server=${encodeURIComponent(
- 'https://oss.sonatype.org'
- )}`
+ 'https://oss.sonatype.org',
+ )}`,
)
t.create('Nexus snapshot')
.get('/s/https/oss.sonatype.org/com.google.guava/guava.svg')
.expectRedirect(
`/nexus/s/com.google.guava/guava.svg?server=${encodeURIComponent(
- 'https://oss.sonatype.org'
- )}`
+ 'https://oss.sonatype.org',
+ )}`,
)
t.create('Nexus repository with query opts')
.get(
- '/fs-public-snapshots/https/repository.jboss.org/nexus/com.progress.fuse/fusehq:p=tar.gz:c=agent-apple-osx.svg'
+ '/fs-public-snapshots/https/repository.jboss.org/nexus/com.progress.fuse/fusehq:p=tar.gz:c=agent-apple-osx.svg',
)
.expectRedirect(
- `/nexus/fs-public-snapshots/com.progress.fuse/fusehq.svg?queryOpt=${encodeURIComponent(
- ':p=tar.gz:c=agent-apple-osx'
- )}&server=${encodeURIComponent('https://repository.jboss.org/nexus')}`
+ `/nexus/fs-public-snapshots/com.progress.fuse/fusehq.svg?server=${encodeURIComponent('https://repository.jboss.org/nexus')}&queryOpt=${encodeURIComponent(
+ ':p=tar.gz:c=agent-apple-osx',
+ )}`,
)
diff --git a/services/nexus/nexus-version.js b/services/nexus/nexus-version.js
deleted file mode 100644
index f612b1e59cec8..0000000000000
--- a/services/nexus/nexus-version.js
+++ /dev/null
@@ -1,10 +0,0 @@
-'use strict'
-
-function isSnapshotVersion(version) {
- const pattern = /(\d+\.)*\d-SNAPSHOT/
- return version && version.match(pattern)
-}
-
-module.exports = {
- isSnapshotVersion,
-}
diff --git a/services/nexus/nexus.service.js b/services/nexus/nexus.service.js
index 54b09e94106c7..ac8c5f50741ce 100644
--- a/services/nexus/nexus.service.js
+++ b/services/nexus/nexus.service.js
@@ -1,174 +1,100 @@
-'use strict'
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { url } from '../validators.js'
+import { BaseJsonService, NotFound, pathParams, queryParams } from '../index.js'
-const Joi = require('@hapi/joi')
-const { version: versionColor } = require('../color-formatters')
-const { addv } = require('../text-formatters')
-const { optionalUrl } = require('../validators')
-const {
- optionalDottedVersionNClausesWithOptionalSuffix,
-} = require('../validators')
-const { BaseJsonService, InvalidResponse, NotFound } = require('..')
-const { isSnapshotVersion } = require('./nexus-version')
-
-const nexus2SearchApiSchema = Joi.object({
- data: Joi.array()
- .items(
- Joi.object({
- latestRelease: optionalDottedVersionNClausesWithOptionalSuffix,
- latestSnapshot: optionalDottedVersionNClausesWithOptionalSuffix,
- // `version` will almost always follow the same pattern as optionalDottedVersionNClausesWithOptionalSuffix.
- // However, there are a couple exceptions where `version` may be a simple string (like `android-SNAPSHOT`)
- // This schema is relaxed accordingly since for snapshot/release badges the schema has to validate
- // the entire history of each published version for the artifact.
- // Example artifact that includes such a historical version: https://oss.sonatype.org/service/local/lucene/search?g=com.google.guava&a=guava
- version: Joi.string(),
- })
- )
- .required(),
-}).required()
-
-const nexus3SearchApiSchema = Joi.object({
+const searchApiSchema = Joi.object({
items: Joi.array()
.items(
Joi.object({
- // This schema is relaxed similarly to nexux2SearchApiSchema
version: Joi.string().required(),
- })
+ }),
)
.required(),
}).required()
-const nexus2ResolveApiSchema = Joi.object({
- data: Joi.object({
- baseVersion: optionalDottedVersionNClausesWithOptionalSuffix,
- version: optionalDottedVersionNClausesWithOptionalSuffix,
- }).required(),
-}).required()
-
const queryParamSchema = Joi.object({
- server: optionalUrl.required(),
+ server: url,
queryOpt: Joi.string()
.regex(/(:[\w.]+=[^:]*)+/i)
.optional(),
- nexusVersion: Joi.equal('2', '3'),
}).required()
-module.exports = class Nexus extends BaseJsonService {
- static get category() {
- return 'version'
- }
+const openApiQueryParams = queryParams(
+ { name: 'server', example: 'https://repo.tomkeuper.com', required: true },
+ {
+ name: 'queryOpt',
+ example: ':c=agent-apple-osx:p=tar.gz',
+ description: `
+Note that you can use query options with any Nexus badge type (Releases, Snapshots, or Repository).
- static get route() {
- return {
- base: 'nexus',
- pattern: ':repo(r|s|[^/]+)/:groupId/:artifactId',
- queryParamSchema,
- }
+Query options should be provided as key=value pairs separated by a colon.
+
+Possible values: Searching for Component
+`,
+ },
+)
+
+export default class Nexus extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'nexus',
+ pattern: ':repo(r|s|[^/]+)/:groupId/:artifactId',
+ queryParamSchema,
}
- static get auth() {
- return { userKey: 'nexus_user', passKey: 'nexus_pass', serviceKey: 'nexus' }
+ static auth = {
+ userKey: 'nexus_user',
+ passKey: 'nexus_pass',
+ serviceKey: 'nexus',
}
- static get examples() {
- return [
- {
- title: 'Sonatype Nexus (Releases)',
- pattern: 'r/:groupId/:artifactId',
- namedParams: {
- groupId: 'org.apache.commons',
- artifactId: 'commons-lang3',
- },
- queryParams: {
- server: 'https://nexus.pentaho.org',
- nexusVersion: '3',
- },
- staticPreview: this.render({
- version: '3.9',
- }),
- documentation: `
-
- Specifying 'nexusVersion=3' when targeting Nexus 3 servers will speed up the badge rendering.
- Note that you can use this query parameter with any Nexus badge type (Releases, Snapshots, or Repository).
-
- `,
- },
- {
- title: 'Sonatype Nexus (Snapshots)',
- pattern: 's/:groupId/:artifactId',
- namedParams: {
- groupId: 'com.google.guava',
- artifactId: 'guava',
- },
- queryParams: {
- server: 'https://oss.sonatype.org',
- },
- staticPreview: this.render({
- version: 'v24.0-SNAPSHOT',
- }),
+ static openApi = {
+ '/nexus/r/{groupId}/{artifactId}': {
+ get: {
+ summary: 'Sonatype Nexus (Releases)',
+ parameters: [
+ ...pathParams(
+ { name: 'groupId', example: 'com.google.guava' },
+ { name: 'artifactId', example: 'guava' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Sonatype Nexus (Repository)',
- pattern: ':repo/:groupId/:artifactId',
- namedParams: {
- repo: 'developer',
- groupId: 'ai.h2o',
- artifactId: 'h2o-automl',
- },
- queryParams: {
- server: 'https://repository.jboss.org/nexus',
- },
- staticPreview: this.render({
- version: '3.22.0.2',
- }),
+ },
+ '/nexus/s/{groupId}/{artifactId}': {
+ get: {
+ summary: 'Sonatype Nexus (Snapshots)',
+ parameters: [
+ ...pathParams(
+ { name: 'groupId', example: 'com.google.guava' },
+ { name: 'artifactId', example: 'guava' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Sonatype Nexus (Query Options)',
- pattern: ':repo/:groupId/:artifactId',
- namedParams: {
- repo: 'fs-public-snapshots',
- groupId: 'com.progress.fuse',
- artifactId: 'fusehq',
- },
- queryParams: {
- server: 'https://repository.jboss.org/nexus',
- queryOpt: ':c=agent-apple-osx:p=tar.gz',
- },
- staticPreview: this.render({
- version: '7.0.1-SNAPSHOT',
- }),
- documentation: `
-
- Note that you can use query options with any Nexus badge type (Releases, Snapshots, or Repository).
-
-
- Query options should be provided as key=value pairs separated by a colon.
-
-
- Possible values:
-
-
- `,
+ },
+ '/nexus/{repo}/{groupId}/{artifactId}': {
+ get: {
+ summary: 'Sonatype Nexus (Repository)',
+ parameters: [
+ ...pathParams(
+ { name: 'repo', example: 'maven-central' },
+ { name: 'groupId', example: 'com.google.guava' },
+ { name: 'artifactId', example: 'guava' },
+ ),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'nexus' }
- }
-
- static render({ version }) {
- return {
- message: addv(version),
- color: versionColor(version),
- }
+ static defaultBadgeData = {
+ label: 'nexus',
}
- addQueryParamsToQueryString({ qs, queryOpt }) {
+ addQueryParamsToQueryString({ searchParams, queryOpt }) {
// Users specify query options with 'key=value' pairs, using a
// colon delimiter between pairs ([:k1=v1[:k2=v2[...]]]).
// queryOpt will be a string containing those key/value pairs,
@@ -178,67 +104,12 @@ module.exports = class Nexus extends BaseJsonService {
const paramParts = keyValuePair.split('=')
const paramKey = paramParts[0]
const paramValue = paramParts[1]
- qs[paramKey] = paramValue
+ searchParams[paramKey] = paramValue
})
}
- async fetch({ server, repo, groupId, artifactId, queryOpt, nexusVersion }) {
- if (nexusVersion === '3') {
- return this.fetch3({ server, repo, groupId, artifactId, queryOpt })
- }
- // Most servers still use Nexus 2. Fall back to Nexus 3 if the hitting a
- // Nexus 2 endpoint returns a Bad Request (=> InvalidResponse, for path /service/local/artifact/maven/resolve)
- // or a Not Found (for path /service/local/artifact/maven/resolve).
- try {
- return await this.fetch2({ server, repo, groupId, artifactId, queryOpt })
- } catch (e) {
- if (e instanceof InvalidResponse || e instanceof NotFound) {
- return this.fetch3({ server, repo, groupId, artifactId, queryOpt })
- }
- throw e
- }
- }
-
- async fetch2({ server, repo, groupId, artifactId, queryOpt }) {
- const qs = {
- g: groupId,
- a: artifactId,
- }
- let schema
- let url = `${server}${server.slice(-1) === '/' ? '' : '/'}`
- // API pattern:
- // for /nexus/[rs]/... pattern, use the search api of the nexus server, and
- // for /nexus//... pattern, use the resolve api of the nexus server.
- if (repo === 'r' || repo === 's') {
- schema = nexus2SearchApiSchema
- url += 'service/local/lucene/search'
- } else {
- schema = nexus2ResolveApiSchema
- url += 'service/local/artifact/maven/resolve'
- qs.r = repo
- qs.v = 'LATEST'
- }
-
- if (queryOpt) {
- this.addQueryParamsToQueryString({ qs, queryOpt })
- }
-
- const json = await this._requestJson(
- this.authHelper.withBasicAuth({
- schema,
- url,
- options: { qs },
- errorMessages: {
- 404: 'artifact not found',
- },
- })
- )
-
- return { actualNexusVersion: '2', json }
- }
-
- async fetch3({ server, repo, groupId, artifactId, queryOpt }) {
- const qs = {
+ async fetch({ server, repo, groupId, artifactId, queryOpt }) {
+ const searchParams = {
group: groupId,
name: artifactId,
sort: 'version',
@@ -246,18 +117,18 @@ module.exports = class Nexus extends BaseJsonService {
switch (repo) {
case 's':
- qs.prerelease = 'true'
+ searchParams.prerelease = 'true'
break
case 'r':
- qs.prerelease = 'false'
+ searchParams.prerelease = 'false'
break
default:
- qs.repository = repo
+ searchParams.repository = repo
break
}
if (queryOpt) {
- this.addQueryParamsToQueryString({ qs, queryOpt })
+ this.addQueryParamsToQueryString({ searchParams, queryOpt })
}
const url = `${server}${
@@ -266,56 +137,19 @@ module.exports = class Nexus extends BaseJsonService {
const json = await this._requestJson(
this.authHelper.withBasicAuth({
- schema: nexus3SearchApiSchema,
+ schema: searchApiSchema,
url,
- options: { qs },
- errorMessages: {
+ options: { searchParams },
+ httpErrors: {
404: 'artifact not found',
},
- })
+ }),
)
- return { actualNexusVersion: '3', json }
- }
-
- transform({ repo, json, actualNexusVersion }) {
- return actualNexusVersion === '3'
- ? this.transform3({ repo, json })
- : this.transform2({ repo, json })
- }
-
- transform2({ repo, json }) {
- if (json.data.length === 0) {
- throw new NotFound({ prettyMessage: 'artifact or version not found' })
- }
- if (repo === 'r') {
- const version = json.data[0].latestRelease
- if (!version) {
- throw new InvalidResponse({ prettyMessage: 'invalid artifact version' })
- }
- return { version }
- } else if (repo === 's') {
- // only want to match 1.2.3-SNAPSHOT style versions, which may not always be in
- // 'latestSnapshot' so check 'version' as well before continuing to next entry
- for (const artifact of json.data) {
- if (isSnapshotVersion(artifact.latestSnapshot)) {
- return { version: artifact.latestSnapshot }
- }
- if (isSnapshotVersion(artifact.version)) {
- return { version: artifact.version }
- }
- }
- throw new InvalidResponse({ prettyMessage: 'no snapshot versions found' })
- } else {
- const version = json.data.baseVersion || json.data.version
- if (!version) {
- throw new InvalidResponse({ prettyMessage: 'invalid artifact version' })
- }
- return { version }
- }
+ return { json }
}
- transform3({ repo, json }) {
+ transform({ repo, json }) {
if (json.items.length === 0) {
const versionType = repo === 's' ? 'snapshot ' : ''
throw new NotFound({
@@ -325,20 +159,16 @@ module.exports = class Nexus extends BaseJsonService {
return { version: json.items[0].version }
}
- async handle(
- { repo, groupId, artifactId },
- { server, queryOpt, nexusVersion }
- ) {
- const { actualNexusVersion, json } = await this.fetch({
+ async handle({ repo, groupId, artifactId }, { server, queryOpt }) {
+ const { json } = await this.fetch({
repo,
server,
groupId,
artifactId,
queryOpt,
- nexusVersion,
})
- const { version } = this.transform({ repo, json, actualNexusVersion })
- return this.constructor.render({ version })
+ const { version } = this.transform({ repo, json })
+ return renderVersionBadge({ version })
}
}
diff --git a/services/nexus/nexus.spec.js b/services/nexus/nexus.spec.js
index b9717ec124650..17a32dfda240a 100644
--- a/services/nexus/nexus.spec.js
+++ b/services/nexus/nexus.spec.js
@@ -1,16 +1,13 @@
-'use strict'
+import { expect } from 'chai'
+import { testAuth } from '../test-helpers.js'
+import { NotFound } from '../index.js'
+import Nexus from './nexus.service.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const { InvalidResponse, NotFound } = require('..')
-const Nexus = require('./nexus.service')
-
-describe('Nexus', function() {
- context('transform2()', function() {
- it('throws NotFound error when no versions exist', function() {
+describe('Nexus', function () {
+ context('transform()', function () {
+ it('throws NotFound error when no items exist', function () {
try {
- Nexus.prototype.transform2({ json: { data: [] } })
+ Nexus.prototype.transform({ json: { items: [] } })
expect.fail('Expected to throw')
} catch (e) {
expect(e).to.be.an.instanceof(NotFound)
@@ -18,149 +15,58 @@ describe('Nexus', function() {
}
})
- it('throws InvalidResponse error when no there is no latestRelease version', function() {
+ it('throws NotFound error when no snapshot items exist', function () {
try {
- Nexus.prototype.transform2({ repo: 'r', json: { data: [{}] } })
+ Nexus.prototype.transform({
+ repo: 's',
+ json: { items: [] },
+ })
expect.fail('Expected to throw')
} catch (e) {
- expect(e).to.be.an.instanceof(InvalidResponse)
- expect(e.prettyMessage).to.equal('invalid artifact version')
+ expect(e).to.be.an.instanceof(NotFound)
+ expect(e.prettyMessage).to.equal(
+ 'artifact or snapshot version not found',
+ )
}
})
- it('returns latestSnapshot value', function() {
- const latestSnapshot = '7.0.1-SNAPSHOT'
- const { version } = Nexus.prototype.transform2({
- repo: 's',
- json: {
- data: [{ latestSnapshot }, { version: '1.2.3' }],
- },
- })
- expect(version).to.equal(latestSnapshot)
- })
-
- it('returns version value when it is a snapshot', function() {
- const latestSnapshot = '1.2.7-SNAPSHOT'
- const { version } = Nexus.prototype.transform2({
- repo: 's',
+ it('returns first item version', function () {
+ const { version } = Nexus.prototype.transform({
json: {
- data: [{ latestSnapshot: '1.2.3' }, { version: latestSnapshot }],
+ items: [
+ { version: '1.2.3' },
+ { version: '1.2.2' },
+ { version: '1.0.1' },
+ ],
},
})
- expect(version).to.equal(latestSnapshot)
- })
-
- it('throws InvalidResponse error when no snapshot versions exist', function() {
- try {
- Nexus.prototype.transform2({ repo: 's', json: { data: [{}] } })
- expect.fail('Expected to throw')
- } catch (e) {
- expect(e).to.be.an.instanceof(InvalidResponse)
- expect(e.prettyMessage).to.equal('no snapshot versions found')
- }
- })
-
- it('throws InvalidResponse error when repository has no version data', function() {
- try {
- Nexus.prototype.transform2({
- repo: 'developer',
- json: { data: {} },
- })
- expect.fail('Expected to throw')
- } catch (e) {
- expect(e).to.be.an.instanceof(InvalidResponse)
- expect(e.prettyMessage).to.equal('invalid artifact version')
- }
- })
-
- context('transform3()', function() {
- it('throws NotFound error when no items exist', function() {
- try {
- Nexus.prototype.transform3({ json: { items: [] } })
- expect.fail('Expected to throw')
- } catch (e) {
- expect(e).to.be.an.instanceof(NotFound)
- expect(e.prettyMessage).to.equal('artifact or version not found')
- }
- })
-
- it('throws NotFound error when no snapshot items exist', function() {
- try {
- Nexus.prototype.transform3({
- repo: 's',
- json: { items: [] },
- })
- expect.fail('Expected to throw')
- } catch (e) {
- expect(e).to.be.an.instanceof(NotFound)
- expect(e.prettyMessage).to.equal(
- 'artifact or snapshot version not found'
- )
- }
- })
-
- it('returns first item version', function() {
- const { version } = Nexus.prototype.transform3({
- json: {
- items: [
- { version: '1.2.3' },
- { version: '1.2.2' },
- { version: '1.0.1' },
- ],
- },
- })
- expect(version).to.equal('1.2.3')
- })
+ expect(version).to.equal('1.2.3')
})
})
- describe('auth', function() {
- cleanUpNockAfterEach()
-
- const user = 'admin'
- const pass = 'password'
+ describe('auth', function () {
const config = {
public: {
services: {
nexus: {
- authorizedOrigins: ['https://repository.jboss.org'],
+ authorizedOrigins: ['https://repo.tomkeuper.com'],
},
},
},
- private: {
- nexus_user: user,
- nexus_pass: pass,
- },
}
-
- it('sends the auth information as configured', async function() {
- const scope = nock('https://repository.jboss.org')
- .get('/nexus/service/local/lucene/search')
- .query({ g: 'jboss', a: 'jboss-client' })
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, { data: [{ latestRelease: '2.3.4' }] })
-
- expect(
- await Nexus.invoke(
- defaultContext,
- config,
- {
- repo: 'r',
- groupId: 'jboss',
- artifactId: 'jboss-client',
- },
- {
- server: 'https://repository.jboss.org/nexus',
- }
- )
- ).to.deep.equal({
- message: 'v2.3.4',
- color: 'blue',
- })
-
- scope.done()
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ Nexus,
+ 'BasicAuth',
+ {
+ items: [
+ {
+ version: '9.3.95',
+ },
+ ],
+ },
+ { configOverride: config },
+ )
})
})
})
diff --git a/services/nexus/nexus.tester.js b/services/nexus/nexus.tester.js
index b227d0906a3bd..c68958a450a4e 100644
--- a/services/nexus/nexus.tester.js
+++ b/services/nexus/nexus.tester.js
@@ -1,307 +1,68 @@
-'use strict'
+import { isVPlusDottedVersionNClausesWithOptionalSuffix as isVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffix: isVersion,
-} = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Nexus 2 - search release version valid artifact')
- .timeout(15000)
- .get('/r/com.google/bitcoinj.json?server=https://oss.sonatype.org')
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create('Nexus 2 - search release version of an nonexistent artifact')
- .timeout(15000)
- .get(
- '/r/com.google.guava/nonexistent-artifact-id.json?server=https://oss.sonatype.org'
- )
- .expectBadge({
- label: 'nexus',
- message: 'artifact or version not found',
- })
-
-t.create('Nexus 2 - search snapshot version valid snapshot artifact')
- .timeout(15000)
- .get(
- '/s/org.fusesource.apollo/apollo-karaf-feature.json?server=https://repository.jboss.org/nexus'
- )
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create('Nexus 2 - search snapshot version of an nonexistent artifact')
- .timeout(15000)
- .get(
- '/s/com.google.guava/nonexistent-artifact-id.json?server=https://oss.sonatype.org'
- )
- .expectBadge({
- label: 'nexus',
- message: 'artifact or version not found',
- color: 'red',
- })
-
-t.create('Nexus 2 - repository version')
- .get('/public/asm/asm.json?server=http://repo.e-iceblue.com/nexus')
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create('Nexus 2 - repository version with query')
- .timeout(15000)
- .get(
- `/fs-public-snapshots/com.progress.fuse/fusehq.json?server=https://repository.jboss.org/nexus&queryOpt=${encodeURIComponent(
- ':p=tar.gz:c=agent-apple-osx'
- )}`
- )
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create('Nexus 2 - repository version of an nonexistent artifact')
- .timeout(15000)
- .get(
- '/developer/jboss/nonexistent-artifact-id.json?server=https://repository.jboss.org/nexus'
- )
- .expectBadge({
- label: 'nexus',
- message: 'artifact not found',
- })
-
-t.create('Nexus 2 - snapshot version with + in version')
- .get(
- '/s/com.progress.fuse/fusehq.json?server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/lucene/search')
- .query({ g: 'com.progress.fuse', a: 'fusehq' })
- .reply(200, { data: [{ version: '7.0.1+19-8844c122-SNAPSHOT' }] })
- )
- .expectBadge({
- label: 'nexus',
- color: 'orange',
- message: isVersion,
- })
-
-t.create('Nexus 2 - search snapshot version not in latestSnapshot')
- .get(
- '/s/com.progress.fuse/fusehq.json?server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/lucene/search')
- .query({ g: 'com.progress.fuse', a: 'fusehq' })
- .reply(200, { data: [{ version: '7.0.1-SNAPSHOT' }] })
- )
- .expectBadge({
- label: 'nexus',
- message: 'v7.0.1-SNAPSHOT',
- color: 'orange',
- })
-
-t.create('Nexus 2 - search snapshot no snapshot versions')
- .get(
- '/s/com.progress.fuse/fusehq.json?server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/lucene/search')
- .query({ g: 'com.progress.fuse', a: 'fusehq' })
- .reply(200, { data: [{ version: '1.2.3' }] })
- )
- .expectBadge({
- label: 'nexus',
- message: 'no snapshot versions found',
- color: 'lightgrey',
- })
-
-t.create('Nexus 2 - search release version')
- .get('/r/jboss/jboss-client.json?server=https://repository.jboss.org/nexus')
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/lucene/search')
- .query({ g: 'jboss', a: 'jboss-client' })
- .reply(200, { data: [{ latestRelease: '1.0.0' }] })
- )
- .expectBadge({
- label: 'nexus',
- message: 'v1.0.0',
- color: 'blue',
- })
-
-t.create('Nexus 2 - repository release version')
- .get(
- '/developer/ai.h2o/h2o-automl.json?server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/artifact/maven/resolve')
- .query({
- g: 'ai.h2o',
- a: 'h2o-automl',
- r: 'developer',
- v: 'LATEST',
- })
- .reply(200, {
- data: {
- baseVersion: '1.2.3',
- version: '1.0.0',
- },
- })
- )
- .expectBadge({
- label: 'nexus',
- message: 'v1.2.3',
- color: 'blue',
- })
-
-t.create('Nexus 2 - repository release version')
- .get(
- '/developer/ai.h2o/h2o-automl.json?server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/artifact/maven/resolve')
- .query({
- g: 'ai.h2o',
- a: 'h2o-automl',
- r: 'developer',
- v: 'LATEST',
- })
- .reply(200, {
- data: {
- version: '1.0.0',
- },
- })
- )
- .expectBadge({
- label: 'nexus',
- message: 'v1.0.0',
- color: 'blue',
- })
-
-t.create('Nexus 2 - user query params')
- .get(
- '/fs-public-snapshots/com.progress.fuse/fusehq.json?queryOpt=:c=agent-apple-osx:p=tar.gz&server=https://repository.jboss.org/nexus'
- )
- .intercept(nock =>
- nock('https://repository.jboss.org/nexus')
- .get('/service/local/artifact/maven/resolve')
- .query({
- g: 'com.progress.fuse',
- a: 'fusehq',
- r: 'fs-public-snapshots',
- v: 'LATEST',
- c: 'agent-apple-osx',
- p: 'tar.gz',
- })
- .reply(200, {
- data: {
- version: '3.2.1',
- },
- })
- )
- .expectBadge({
- label: 'nexus',
- message: 'v3.2.1',
- color: 'blue',
- })
-
-t.create('Nexus 3 - search release version valid artifact')
- .get(
- '/r/org.apache.commons/commons-lang3.json?server=https://nexus.pentaho.org&nexusVersion=3'
- )
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create(
- 'Nexus 3 - search release version valid artifact without explicit nexusVersion parameter'
-)
- .timeout(15000)
- .get(
- '/r/org.apache.commons/commons-lang3.json?server=https://nexus.pentaho.org'
- )
+t.create('search release version valid artifact')
+ .get('/r/me.neznamy/tab-api.json?server=https://repo.tomkeuper.com')
.expectBadge({
label: 'nexus',
message: isVersion,
})
-t.create('Nexus 3 - search release version of an nonexistent artifact')
+t.create('search release version of an nonexistent artifact')
.get(
- '/r/org.apache.commons/nonexistent-artifact-id.json?server=https://nexus.pentaho.org&nexusVersion=3'
+ '/r/me.neznamy/nonexistent-artifact-id.json?server=https://repo.tomkeuper.com',
)
.expectBadge({
label: 'nexus',
message: 'artifact or version not found',
})
-t.create('Nexus 3 - search snapshot version valid snapshot artifact')
+t.create('search snapshot version valid snapshot artifact')
.get(
- '/s/org.pentaho/pentaho-registry.json?server=https://nexus.pentaho.org&nexusVersion=3'
+ '/s/io.github.beefdev.uuidswitcher/common.json?server=https://repo.codemc.io',
)
.expectBadge({
label: 'nexus',
message: isVersion,
})
-t.create('Nexus 3 - search snapshot version for artifact without snapshots')
- .get(
- '/s/javax.inject/javax.inject.json?server=https://nexus.pentaho.org&nexusVersion=3'
- )
+t.create('search snapshot version for artifact without snapshots')
+ .get('/s/com.tomkeuper/spigot.json?server=https://repo.tomkeuper.com')
.expectBadge({
label: 'nexus',
message: 'artifact or snapshot version not found',
color: 'red',
})
-t.create('Nexus 3 - repository version')
- .get(
- '/proxy-public-3rd-party-release/com.h2database/h2.json?server=https://nexus.pentaho.org&nexusVersion=3'
- )
- .expectBadge({
- label: 'nexus',
- message: isVersion,
- })
-
-t.create(
- 'Nexus 3 - repository version valid artifact without explicit nexusVersion parameter'
-)
- .timeout(15000)
+t.create('repository version')
.get(
- '/proxy-public-3rd-party-release/com.h2database/h2.json?server=https://nexus.pentaho.org'
+ '/bedwars-releases/me.neznamy/tab-api.json?server=https://repo.tomkeuper.com',
)
.expectBadge({
label: 'nexus',
message: isVersion,
})
-t.create('Nexus 3 - repository version with query')
+t.create('repository version with query')
.get(
- `/proxy-public-3rd-party-release/org.junit.jupiter/junit-jupiter.json?server=https://nexus.pentaho.org&nexusVersion=3&queryOpt=${encodeURIComponent(
- ':maven.extension=jar:direction=asc'
- )}`
+ `/bedwars-releases/me.neznamy/tab-api.json?server=https://repo.tomkeuper.com&queryOpt=${encodeURIComponent(
+ ':maven.extension=jar:direction=asc',
+ )}`,
)
.expectBadge({
label: 'nexus',
message: isVersion,
})
-t.create('Nexus 3 - search release version without snapshots')
+t.create('search release version without snapshots')
.get(
// Limit the version from above, so that any later artifacts don't break this test.
- `/r/org.pentaho.adaptive/daemon.json?server=https://nexus.pentaho.org&nexusVersion=3&queryOpt=${encodeURIComponent(
- ':maven.baseVersion=<8.1.0.1'
- )}`
+ `/r/net.pistonmaster/pistonmotd-bukkit.json?server=https://repo.codemc.io&queryOpt=${encodeURIComponent(
+ ':maven.baseVersion=<5.1.1',
+ )}`,
)
.expectBadge({
label: 'nexus',
- message: 'v8.1.0.0-365',
+ message: 'v5.1.0',
})
diff --git a/services/node/node-base.js b/services/node/node-base.js
index a0b9e91275710..7daf26f32de41 100644
--- a/services/node/node-base.js
+++ b/services/node/node-base.js
@@ -1,95 +1,7 @@
-'use strict'
+import NPMBase from '../npm/npm-base.js'
-const NPMBase = require('../npm/npm-base')
-
-const keywords = ['npm']
-
-module.exports = class NodeVersionBase extends NPMBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return this.buildRoute(`node/${this.path}`, { withTag: true })
- }
-
- static get examples() {
- const type = this.type
- const documentation = `
-
- ${this.documentation}
- The node version support is retrieved from the engines.node section in package.json.
-
-`
- const prefix = `node-${type}`
- return [
- {
- title: `${prefix}`,
- pattern: ':packageName',
- namedParams: { packageName: 'passport' },
- staticPreview: this.renderStaticPreview({
- nodeVersionRange: '>= 6.0.0',
- }),
- keywords,
- documentation,
- },
- {
- title: `${prefix} (scoped)`,
- pattern: '@:scope/:packageName',
- namedParams: { scope: 'stdlib', packageName: 'stdlib' },
- staticPreview: this.renderStaticPreview({
- nodeVersionRange: '>= 6.0.0',
- }),
- keywords,
- documentation,
- },
- {
- title: `${prefix} (tag)`,
- pattern: ':packageName/:tag',
- namedParams: { packageName: 'passport', tag: 'latest' },
- staticPreview: this.renderStaticPreview({
- nodeVersionRange: '>= 6.0.0',
- tag: 'latest',
- }),
- keywords,
- documentation,
- },
- {
- title: `${prefix} (scoped with tag)`,
- pattern: '@:scope/:packageName/:tag',
- namedParams: { scope: 'stdlib', packageName: 'stdlib', tag: 'latest' },
- staticPreview: this.renderStaticPreview({
- nodeVersionRange: '>= 6.0.0',
- tag: 'latest',
- }),
- keywords,
- documentation,
- },
- {
- title: `${prefix} (scoped with tag, custom registry)`,
- pattern: '@:scope/:packageName/:tag',
- namedParams: { scope: 'stdlib', packageName: 'stdlib', tag: 'latest' },
- queryParams: { registry_uri: 'https://registry.npmjs.com' },
- staticPreview: this.renderStaticPreview({
- nodeVersionRange: '>= 6.0.0',
- tag: 'latest',
- }),
- keywords,
- documentation,
- },
- ]
- }
-
- static renderStaticPreview({ tag, nodeVersionRange }) {
- // Since this badge has an async `render()` function, but `get examples()` has to
- // be synchronous, this method exists. It should return the same value as the
- // real `render()`. There's a unit test to check that.
- return {
- label: tag ? `${this.defaultBadgeData.label}@${tag}` : undefined,
- message: nodeVersionRange,
- color: 'brightgreen',
- }
- }
+export default class NodeVersionBase extends NPMBase {
+ static category = 'platform-support'
static async render({ tag, nodeVersionRange }) {
// Atypically, the `render()` function of this badge is `async` because it needs to pull
@@ -112,12 +24,8 @@ module.exports = class NodeVersionBase extends NPMBase {
}
async handle(namedParams, queryParams) {
- const {
- scope,
- packageName,
- tag,
- registryUrl,
- } = this.constructor.unpackParams(namedParams, queryParams)
+ const { scope, packageName, tag, registryUrl } =
+ this.constructor.unpackParams(namedParams, queryParams)
const { engines } = await this.fetchPackageData({
scope,
packageName,
diff --git a/services/node/node-current.service.js b/services/node/node-current.service.js
index 62ea26c4b521d..9cb8b09e38c10 100644
--- a/services/node/node-current.service.js
+++ b/services/node/node-current.service.js
@@ -1,26 +1,60 @@
-'use strict'
+import { pathParam, queryParam } from '../index.js'
+import { packageNameDescription } from '../npm/npm-base.js'
+import NodeVersionBase from './node-base.js'
+import { versionColorForRangeCurrent } from './node-version-color.js'
-const NodeVersionBase = require('./node-base')
-const { versionColorForRangeCurrent } = require('./node-version-color')
+const description = `This badge indicates whether the package supports the latest release of node.
+The node version support is retrieved from the engines.node section in package.json.
`
-module.exports = class NodeCurrentVersion extends NodeVersionBase {
- static get path() {
- return 'v'
- }
+export default class NodeCurrentVersion extends NodeVersionBase {
+ static route = this.buildRoute('node/v', { withTag: true })
- static get defaultBadgeData() {
- return { label: 'node' }
+ static openApi = {
+ '/node/v/{packageName}': {
+ get: {
+ summary: 'Node Current',
+ description,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'passport',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ '/node/v/{packageName}/{tag}': {
+ get: {
+ summary: 'Node Current (with tag)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'passport',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'tag',
+ example: 'latest',
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
}
- static get type() {
- return 'current'
+ static defaultBadgeData = {
+ label: 'node',
}
- static get colorResolver() {
- return versionColorForRangeCurrent
- }
+ static type = 'current'
- static get documentation() {
- return `This badge indicates whether the package supports the latest release of node`
- }
+ static colorResolver = versionColorForRangeCurrent
}
diff --git a/services/node/node-current.spec.js b/services/node/node-current.spec.js
deleted file mode 100644
index a0a9e46e4b605..0000000000000
--- a/services/node/node-current.spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const NodeVersion = require('./node-current.service')
-
-describe('node static renderStaticPreview', function() {
- it('should have parity with render()', async function() {
- const nodeVersionRange = '>= 6.0.0'
-
- const expectedNoTag = await NodeVersion.renderStaticPreview({
- nodeVersionRange,
- })
- const expectedLatestTag = await NodeVersion.renderStaticPreview({
- nodeVersionRange,
- tag: 'latest',
- })
-
- test(NodeVersion.renderStaticPreview.bind(NodeVersion), () => {
- given({ nodeVersionRange }).expect(expectedNoTag)
- given({ nodeVersionRange, tag: 'latest' }).expect(expectedLatestTag)
- })
- })
-})
diff --git a/services/node/node-current.tester.js b/services/node/node-current.tester.js
index b9ae1d9e55cb1..e82d24743679d 100644
--- a/services/node/node-current.tester.js
+++ b/services/node/node-current.tester.js
@@ -1,9 +1,9 @@
-'use strict'
-
-const { expect } = require('chai')
-const { Range } = require('semver')
-const t = (module.exports = require('../tester').createServiceTester())
-const { mockPackageData, mockCurrentSha } = require('./testUtils/test-utils')
+import { expect } from 'chai'
+import semverModule from 'semver'
+import { createServiceTester } from '../tester.js'
+import { mockPackageData, mockCurrentSha } from './testUtils/test-utils.js'
+const { Range } = semverModule
+export const t = await createServiceTester()
function expectSemverRange(message) {
expect(() => new Range(message)).not.to.throw()
@@ -22,10 +22,10 @@ t.create('engines satisfies current node version')
mockPackageData({
packageName: 'passport',
engines: '>=0.4.0',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node', message: `>=0.4.0`, color: `brightgreen` })
+ .expectBadge({ label: 'node', message: '>=0.4.0', color: 'brightgreen' })
t.create('engines does not satisfy current node version')
.get('/passport.json')
@@ -33,10 +33,10 @@ t.create('engines does not satisfy current node version')
mockPackageData({
packageName: 'passport',
engines: '12',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node', message: `12`, color: `yellow` })
+ .expectBadge({ label: 'node', message: '12', color: 'yellow' })
t.create('gets the node version of @stdlib/stdlib')
.get('/@stdlib/stdlib.json')
@@ -54,10 +54,10 @@ t.create('engines satisfies current node version - scoped')
scope: '@stdlib',
tag: '',
registry: '',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node', message: `>=0.4.0`, color: `brightgreen` })
+ .expectBadge({ label: 'node', message: '>=0.4.0', color: 'brightgreen' })
t.create('engines does not satisfy current node version - scoped')
.get('/@stdlib/stdlib.json')
@@ -68,10 +68,10 @@ t.create('engines does not satisfy current node version - scoped')
scope: '@stdlib',
tag: '',
registry: '',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node', message: `12`, color: `yellow` })
+ .expectBadge({ label: 'node', message: '12', color: 'yellow' })
t.create("gets the tagged release's node version version of ionic")
.get('/ionic/testing.json')
@@ -87,13 +87,13 @@ t.create('engines satisfies current node version - tagged')
packageName: 'ionic',
engines: '>=0.4.0',
tag: 'testing',
- })
+ }),
)
.intercept(mockCurrentSha(13))
.expectBadge({
label: 'node@testing',
- message: `>=0.4.0`,
- color: `brightgreen`,
+ message: '>=0.4.0',
+ color: 'brightgreen',
})
t.create('engines does not satisfy current node version - tagged')
@@ -103,10 +103,10 @@ t.create('engines does not satisfy current node version - tagged')
packageName: 'ionic',
engines: '12',
tag: 'testing',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node@testing', message: `12`, color: `yellow` })
+ .expectBadge({ label: 'node@testing', message: '12', color: 'yellow' })
t.create("gets the tagged release's node version of @cycle/core")
.get('/@cycle/core/canary.json')
@@ -123,13 +123,13 @@ t.create('engines satisfies current node version - scoped and tagged')
engines: '>=0.4.0',
scope: '@cycle',
tag: 'canary',
- })
+ }),
)
.intercept(mockCurrentSha(13))
.expectBadge({
label: 'node@canary',
- message: `>=0.4.0`,
- color: `brightgreen`,
+ message: '>=0.4.0',
+ color: 'brightgreen',
})
t.create('engines does not satisfy current node version - scoped and tagged')
@@ -140,10 +140,10 @@ t.create('engines does not satisfy current node version - scoped and tagged')
engines: '12',
scope: '@cycle',
tag: 'canary',
- })
+ }),
)
.intercept(mockCurrentSha(13))
- .expectBadge({ label: 'node@canary', message: `12`, color: `yellow` })
+ .expectBadge({ label: 'node@canary', message: '12', color: 'yellow' })
t.create('gets the node version of passport from a custom registry')
.get('/passport.json?registry_uri=https://registry.npmjs.com')
diff --git a/services/node/node-lts.service.js b/services/node/node-lts.service.js
index cdef9af0470bd..a095411d83431 100644
--- a/services/node/node-lts.service.js
+++ b/services/node/node-lts.service.js
@@ -1,26 +1,60 @@
-'use strict'
+import { pathParam, queryParam } from '../index.js'
+import { packageNameDescription } from '../npm/npm-base.js'
+import NodeVersionBase from './node-base.js'
+import { versionColorForRangeLts } from './node-version-color.js'
-const NodeVersionBase = require('./node-base')
-const { versionColorForRangeLts } = require('./node-version-color')
+const description = `This badge indicates whether the package supports all LTS node versions.
+The node version support is retrieved from the engines.node section in package.json.
`
-module.exports = class NodeLtsVersion extends NodeVersionBase {
- static get path() {
- return 'v-lts'
- }
+export default class NodeLtsVersion extends NodeVersionBase {
+ static route = this.buildRoute('node/v-lts', { withTag: true })
- static get defaultBadgeData() {
- return { label: 'node-lts' }
+ static openApi = {
+ '/node/v-lts/{packageName}': {
+ get: {
+ summary: 'Node LTS',
+ description,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'passport',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ '/node/v-lts/{packageName}/{tag}': {
+ get: {
+ summary: 'Node LTS (with tag)',
+ description,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'passport',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'tag',
+ example: 'latest',
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
}
- static get type() {
- return 'lts'
+ static defaultBadgeData = {
+ label: 'node-lts',
}
- static get colorResolver() {
- return versionColorForRangeLts
- }
+ static type = 'lts'
- static get documentation() {
- return `This badge indicates whether the package supports all LTS node versions`
- }
+ static colorResolver = versionColorForRangeLts
}
diff --git a/services/node/node-lts.spec.js b/services/node/node-lts.spec.js
deleted file mode 100644
index 7dfc5ad0bc8cd..0000000000000
--- a/services/node/node-lts.spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const NodeVersion = require('./node-lts.service')
-
-describe('node-lts renderStaticPreview', function() {
- it('should have parity with render()', async function() {
- const nodeVersionRange = '>= 6.0.0'
-
- const expectedNoTag = await NodeVersion.renderStaticPreview({
- nodeVersionRange,
- })
- const expectedLatestTag = await NodeVersion.renderStaticPreview({
- nodeVersionRange,
- tag: 'latest',
- })
-
- test(NodeVersion.renderStaticPreview.bind(NodeVersion), () => {
- given({ nodeVersionRange }).expect(expectedNoTag)
- given({ nodeVersionRange, tag: 'latest' }).expect(expectedLatestTag)
- })
- })
-})
diff --git a/services/node/node-lts.tester.js b/services/node/node-lts.tester.js
index f2d47331fee54..0592b1b5aebc4 100644
--- a/services/node/node-lts.tester.js
+++ b/services/node/node-lts.tester.js
@@ -1,13 +1,13 @@
-'use strict'
-
-const { expect } = require('chai')
-const { Range } = require('semver')
-const t = (module.exports = require('../tester').createServiceTester())
-const {
+import { expect } from 'chai'
+import semverModule from 'semver'
+import { createServiceTester } from '../tester.js'
+import {
mockPackageData,
mockReleaseSchedule,
mockVersionsSha,
-} = require('./testUtils/test-utils')
+} from './testUtils/test-utils.js'
+const { Range } = semverModule
+export const t = await createServiceTester()
function expectSemverRange(message) {
expect(() => new Range(message)).not.to.throw()
@@ -27,10 +27,10 @@ t.create('engines satisfies all lts node versions')
mockPackageData({
packageName: 'passport',
engines: '10 - 12',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `10 - 12`, color: `brightgreen` })
+ .expectBadge({ label: 'node-lts', message: '10 - 12', color: 'brightgreen' })
t.create('engines does not satisfy all lts node versions')
.get('/passport.json')
@@ -39,10 +39,10 @@ t.create('engines does not satisfy all lts node versions')
mockPackageData({
packageName: 'passport',
engines: '8',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `8`, color: `orange` })
+ .expectBadge({ label: 'node-lts', message: '8', color: 'orange' })
t.create('engines satisfies some lts node versions')
.get('/passport.json')
@@ -51,10 +51,10 @@ t.create('engines satisfies some lts node versions')
mockPackageData({
packageName: 'passport',
engines: '10',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `10`, color: `yellow` })
+ .expectBadge({ label: 'node-lts', message: '10', color: 'yellow' })
t.create('gets the node version of @stdlib/stdlib')
.get('/@stdlib/stdlib.json')
@@ -71,10 +71,10 @@ t.create('engines satisfies all lts node versions - scoped')
packageName: 'stdlib',
engines: '10 - 12',
scope: '@stdlib',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `10 - 12`, color: `brightgreen` })
+ .expectBadge({ label: 'node-lts', message: '10 - 12', color: 'brightgreen' })
t.create('engines does not satisfy all lts node versions - scoped')
.get('/@stdlib/stdlib.json')
@@ -84,10 +84,10 @@ t.create('engines does not satisfy all lts node versions - scoped')
packageName: 'stdlib',
engines: '8',
scope: '@stdlib',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `8`, color: `orange` })
+ .expectBadge({ label: 'node-lts', message: '8', color: 'orange' })
t.create('engines satisfies some lts node versions - scoped')
.get('/@stdlib/stdlib.json')
@@ -97,10 +97,10 @@ t.create('engines satisfies some lts node versions - scoped')
packageName: 'stdlib',
engines: '10',
scope: '@stdlib',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts', message: `10`, color: `yellow` })
+ .expectBadge({ label: 'node-lts', message: '10', color: 'yellow' })
t.create("gets the tagged release's node version version of ionic")
.get('/ionic/testing.json')
@@ -117,13 +117,13 @@ t.create('engines satisfies all lts node versions - tagged')
packageName: 'ionic',
engines: '10 - 12',
tag: 'testing',
- })
+ }),
)
.intercept(mockVersionsSha())
.expectBadge({
label: 'node-lts@testing',
- message: `10 - 12`,
- color: `brightgreen`,
+ message: '10 - 12',
+ color: 'brightgreen',
})
t.create('engines does not satisfy all lts node versions - tagged')
@@ -134,10 +134,10 @@ t.create('engines does not satisfy all lts node versions - tagged')
packageName: 'ionic',
engines: '8',
tag: 'testing',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts@testing', message: `8`, color: `orange` })
+ .expectBadge({ label: 'node-lts@testing', message: '8', color: 'orange' })
t.create('engines satisfies some lts node versions - tagged')
.get('/ionic/testing.json')
@@ -147,10 +147,10 @@ t.create('engines satisfies some lts node versions - tagged')
packageName: 'ionic',
engines: '10',
tag: 'testing',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts@testing', message: `10`, color: `yellow` })
+ .expectBadge({ label: 'node-lts@testing', message: '10', color: 'yellow' })
t.create("gets the tagged release's node version of @cycle/core")
.get('/@cycle/core/canary.json')
@@ -168,13 +168,13 @@ t.create('engines satisfies all lts node versions - scoped and tagged')
engines: '10 - 12',
scope: '@cycle',
tag: 'canary',
- })
+ }),
)
.intercept(mockVersionsSha())
.expectBadge({
label: 'node-lts@canary',
- message: `10 - 12`,
- color: `brightgreen`,
+ message: '10 - 12',
+ color: 'brightgreen',
})
t.create('engines does not satisfy all lts node versions - scoped and tagged')
@@ -186,10 +186,10 @@ t.create('engines does not satisfy all lts node versions - scoped and tagged')
engines: '8',
scope: '@cycle',
tag: 'canary',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts@canary', message: `8`, color: `orange` })
+ .expectBadge({ label: 'node-lts@canary', message: '8', color: 'orange' })
t.create('engines satisfies some lts node versions - scoped and tagged')
.get('/@cycle/core/canary.json')
@@ -200,10 +200,10 @@ t.create('engines satisfies some lts node versions - scoped and tagged')
engines: '10',
scope: '@cycle',
tag: 'canary',
- })
+ }),
)
.intercept(mockVersionsSha())
- .expectBadge({ label: 'node-lts@canary', message: `10`, color: `yellow` })
+ .expectBadge({ label: 'node-lts@canary', message: '10', color: 'yellow' })
t.create('gets the node version of passport from a custom registry')
.get('/passport.json?registry_uri=https://registry.npmjs.com')
diff --git a/services/node/node-version-color.js b/services/node/node-version-color.js
index 9208bcbce0593..78f50f2fa3d1e 100644
--- a/services/node/node-version-color.js
+++ b/services/node/node-version-color.js
@@ -1,20 +1,16 @@
-'use strict'
-
-const { promisify } = require('util')
-const moment = require('moment')
-const semver = require('semver')
-const { regularUpdate } = require('../../core/legacy/regular-update')
+import dayjs from 'dayjs'
+import semver from 'semver'
+import { getCachedResource } from '../../core/base-service/resource-cache.js'
const dateFormat = 'YYYY-MM-DD'
-function getVersion(version) {
- let semver = ``
+async function getVersion(version) {
+ let semver = ''
if (version) {
semver = `-${version}.x`
}
- return promisify(regularUpdate)({
+ return getCachedResource({
url: `https://nodejs.org/dist/latest${semver}/SHASUMS256.txt`,
- intervalMillis: 24 * 3600 * 1000,
json: false,
scraper: shasums => {
// tarball index start, tarball index end
@@ -27,8 +23,8 @@ function getVersion(version) {
}
function ltsVersionsScraper(versions) {
- const currentDate = moment().format(dateFormat)
- return Object.keys(versions).filter(function(version) {
+ const currentDate = dayjs().format(dateFormat)
+ return Object.keys(versions).filter(function (version) {
const data = versions[version]
return data.lts && data.lts < currentDate && data.end > currentDate
})
@@ -39,11 +35,8 @@ async function getCurrentVersion() {
}
async function getLtsVersions() {
- const versions = await promisify(regularUpdate)({
- url:
- 'https://raw.githubusercontent.com/nodejs/Release/master/schedule.json',
- intervalMillis: 24 * 3600 * 1000,
- json: true,
+ const versions = await getCachedResource({
+ url: 'https://raw.githubusercontent.com/nodejs/Release/master/schedule.json',
scraper: ltsVersionsScraper,
})
return Promise.all(versions.map(getVersion))
@@ -52,10 +45,10 @@ async function getLtsVersions() {
async function versionColorForRangeLts(range) {
const ltsVersions = await getLtsVersions()
try {
- const matchesAll = ltsVersions.reduce(function(satisfies, version) {
+ const matchesAll = ltsVersions.reduce(function (satisfies, version) {
return satisfies && semver.satisfies(version, range)
}, true)
- const matchesSome = ltsVersions.reduce(function(satisfies, version) {
+ const matchesSome = ltsVersions.reduce(function (satisfies, version) {
return satisfies || semver.satisfies(version, range)
}, false)
if (matchesAll) {
@@ -85,7 +78,4 @@ async function versionColorForRangeCurrent(range) {
}
}
-module.exports = {
- versionColorForRangeCurrent,
- versionColorForRangeLts,
-}
+export { versionColorForRangeCurrent, versionColorForRangeLts }
diff --git a/services/node/testUtils/test-utils.js b/services/node/testUtils/test-utils.js
index bc0512539d519..d6ee3a6a3e673 100644
--- a/services/node/testUtils/test-utils.js
+++ b/services/node/testUtils/test-utils.js
@@ -1,50 +1,57 @@
-'use strict'
-
-const fs = require('fs')
-const path = require('path')
-const moment = require('moment')
+import fs from 'fs'
+import path from 'path'
+import { fileURLToPath } from 'url'
+import dayjs from 'dayjs'
const dateFormat = 'YYYY-MM-DD'
const templates = {
packageJsonVersionsTemplate: fs.readFileSync(
- path.join(__dirname, `packageJsonVersionsTemplate.json`),
- 'utf-8'
+ path.join(
+ path.dirname(fileURLToPath(import.meta.url)),
+ 'packageJsonVersionsTemplate.json',
+ ),
+ 'utf-8',
),
packageJsonTemplate: fs.readFileSync(
- path.join(__dirname, `packageJsonTemplate.json`),
- 'utf-8'
+ path.join(
+ path.dirname(fileURLToPath(import.meta.url)),
+ 'packageJsonTemplate.json',
+ ),
+ 'utf-8',
),
}
const getTemplate = template => JSON.parse(templates[template])
-const mockPackageData = ({ packageName, engines, scope, tag }) => nock => {
- let packageJson
- let urlPath
- if (scope || tag) {
- if (scope) {
- urlPath = `/${scope}%2F${packageName}`
+const mockPackageData =
+ ({ packageName, engines, scope, tag }) =>
+ nock => {
+ let packageJson
+ let urlPath
+ if (scope || tag) {
+ if (scope) {
+ urlPath = `/${scope}%2F${packageName}`
+ } else {
+ urlPath = `/${packageName}`
+ }
+ packageJson = getTemplate('packageJsonVersionsTemplate')
+ packageJson['dist-tags'][tag || 'latest'] = '0.0.91'
+ packageJson.versions['0.0.91'].engines.node = engines
} else {
- urlPath = `/${packageName}`
+ urlPath = `/${packageName}/latest`
+ packageJson = getTemplate('packageJsonTemplate')
+ packageJson.engines.node = engines
}
- packageJson = getTemplate('packageJsonVersionsTemplate')
- packageJson['dist-tags'][tag || 'latest'] = '0.0.91'
- packageJson.versions['0.0.91'].engines.node = engines
- } else {
- urlPath = `/${packageName}/latest`
- packageJson = getTemplate('packageJsonTemplate')
- packageJson.engines.node = engines
+ return nock('https://registry.npmjs.org/')
+ .get(urlPath)
+ .reply(200, packageJson)
}
- return nock('https://registry.npmjs.org/')
- .get(urlPath)
- .reply(200, packageJson)
-}
const mockCurrentSha = latestVersion => nock => {
const latestSha = `node-v${latestVersion}.12.0-aix-ppc64.tar.gz`
return nock('https://nodejs.org/dist/')
- .get(`/latest/SHASUMS256.txt`)
+ .get('/latest/SHASUMS256.txt')
.reply(200, latestSha)
}
@@ -60,7 +67,7 @@ const mockVersionsSha = () => nock => {
}
const mockReleaseSchedule = () => nock => {
- const currentDate = moment()
+ const currentDate = dayjs()
const schedule = {
'v0.10': {
start: '2013-03-11',
@@ -108,15 +115,9 @@ const mockReleaseSchedule = () => nock => {
},
v10: {
start: '2018-04-24',
- lts: currentDate
- .clone()
- .subtract(6, 'month')
- .format(dateFormat),
+ lts: currentDate.clone().subtract(6, 'month').format(dateFormat),
maintenance: '2020-04-30',
- end: currentDate
- .clone()
- .add(1, 'month')
- .format(dateFormat),
+ end: currentDate.clone().add(1, 'month').format(dateFormat),
codename: 'Dubnium',
},
v11: {
@@ -126,15 +127,9 @@ const mockReleaseSchedule = () => nock => {
},
v12: {
start: '2019-04-23',
- lts: currentDate
- .clone()
- .subtract(1, 'month')
- .format(dateFormat),
+ lts: currentDate.clone().subtract(1, 'month').format(dateFormat),
maintenance: '2020-10-20',
- end: currentDate
- .clone()
- .add(6, 'month')
- .format(dateFormat),
+ end: currentDate.clone().add(6, 'month').format(dateFormat),
codename: 'Erbium',
},
v13: {
@@ -144,26 +139,15 @@ const mockReleaseSchedule = () => nock => {
},
v14: {
start: '2020-04-21',
- lts: currentDate
- .clone()
- .add(4, 'month')
- .format(dateFormat),
+ lts: currentDate.clone().add(4, 'month').format(dateFormat),
maintenance: '2021-10-19',
- end: currentDate
- .clone()
- .add(12, 'month')
- .format(dateFormat),
+ end: currentDate.clone().add(12, 'month').format(dateFormat),
codename: '',
},
}
return nock('https://raw.githubusercontent.com/')
- .get(`/nodejs/Release/master/schedule.json`)
+ .get('/nodejs/Release/master/schedule.json')
.reply(200, schedule)
}
-module.exports = {
- mockPackageData,
- mockCurrentSha,
- mockVersionsSha,
- mockReleaseSchedule,
-}
+export { mockPackageData, mockCurrentSha, mockVersionsSha, mockReleaseSchedule }
diff --git a/services/nodeping/nodeping-status.service.js b/services/nodeping/nodeping-status.service.js
index adac759a1633d..cf90f7dd6ead9 100644
--- a/services/nodeping/nodeping-status.service.js
+++ b/services/nodeping/nodeping-status.service.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
+import Joi from 'joi'
+import {
queryParamSchema,
- exampleQueryParams,
+ queryParams,
renderWebsiteStatus,
-} = require('../website-status')
-const { BaseJsonService } = require('..')
+} from '../website-status.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.array()
.items(Joi.object().keys({ su: Joi.boolean() }))
@@ -17,44 +15,35 @@ const schema = Joi.array()
*/
const exampleCheckUuid = 'jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei'
-module.exports = class NodePingStatus extends BaseJsonService {
- static get category() {
- return 'monitoring'
- }
+export default class NodePingStatus extends BaseJsonService {
+ static category = 'monitoring'
- static get route() {
- return {
- base: 'nodeping/status',
- pattern: ':checkUuid',
- queryParamSchema,
- }
+ static route = {
+ base: 'nodeping/status',
+ pattern: ':checkUuid',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'NodePing status',
- namedParams: {
- checkUuid: exampleCheckUuid,
- },
- queryParams: exampleQueryParams,
- staticPreview: renderWebsiteStatus({ isUp: true }),
+ static openApi = {
+ '/nodeping/status/{checkUuid}': {
+ get: {
+ summary: 'NodePing status',
+ parameters: pathParams({
+ name: 'checkUuid',
+ example: exampleCheckUuid,
+ }).concat(queryParams),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'Status',
- }
- }
+ static defaultBadgeData = { label: 'status' }
async fetch({ checkUuid }) {
const rows = await this._requestJson({
schema,
url: `https://nodeping.com/reports/results/${checkUuid}/1`,
options: {
- qs: { format: 'json' },
+ searchParams: { format: 'json' },
headers: {
'cache-control': 'no-cache',
},
@@ -70,7 +59,7 @@ module.exports = class NodePingStatus extends BaseJsonService {
down_message: downMessage,
up_color: upColor,
down_color: downColor,
- }
+ },
) {
const { isUp } = await this.fetch({ checkUuid })
return renderWebsiteStatus({
diff --git a/services/nodeping/nodeping-status.tester.js b/services/nodeping/nodeping-status.tester.js
index 464e4e70a87c0..9cc586322bd66 100644
--- a/services/nodeping/nodeping-status.tester.js
+++ b/services/nodeping/nodeping-status.tester.js
@@ -1,73 +1,55 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('NodePing status')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
- .expectBadge({
- label: 'Status',
- message: Joi.equal('up', 'down').required(),
- })
+ .expectBadge({ label: 'status', message: Joi.equal('up', 'down').required() })
t.create('NodePing status - up')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
.intercept(nock =>
nock('https://nodeping.com')
.get(
- '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json'
+ '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json',
)
- .reply(200, [{ su: true }])
+ .reply(200, [{ su: true }]),
)
- .expectBadge({
- label: 'Status',
- message: 'up',
- })
+ .expectBadge({ label: 'status', message: 'up' })
t.create('NodePing status - down')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
.intercept(nock =>
nock('https://nodeping.com')
.get(
- '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json'
+ '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json',
)
- .reply(200, [{ su: false }])
+ .reply(200, [{ su: false }]),
)
- .expectBadge({
- label: 'Status',
- message: 'down',
- })
+ .expectBadge({ label: 'status', message: 'down' })
t.create('NodePing status - custom up color/message')
.get(
- '/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json?up_color=blue&up_message=happy'
+ '/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json?up_color=blue&up_message=happy',
)
.intercept(nock =>
nock('https://nodeping.com')
.get(
- '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json'
+ '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json',
)
- .reply(200, [{ su: true }])
+ .reply(200, [{ su: true }]),
)
- .expectBadge({
- label: 'Status',
- message: 'happy',
- color: 'blue',
- })
+ .expectBadge({ label: 'status', message: 'happy', color: 'blue' })
t.create('NodePing status - custom down color/message')
.get(
- '/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json?down_color=yellow&down_message=sad'
+ '/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json?down_color=yellow&down_message=sad',
)
.intercept(nock =>
nock('https://nodeping.com')
.get(
- '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json'
+ '/reports/results/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei/1?format=json',
)
- .reply(200, [{ su: false }])
+ .reply(200, [{ su: false }]),
)
- .expectBadge({
- label: 'Status',
- message: 'sad',
- color: 'yellow',
- })
+ .expectBadge({ label: 'status', message: 'sad', color: 'yellow' })
diff --git a/services/nodeping/nodeping-uptime.service.js b/services/nodeping/nodeping-uptime.service.js
index 8f744667376af..ad7f1aefb1e91 100644
--- a/services/nodeping/nodeping-uptime.service.js
+++ b/services/nodeping/nodeping-uptime.service.js
@@ -1,21 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { colorScale } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const colorFormatter = colorScale([99, 99.5, 100])
const rowSchema = Joi.object().keys({
- uptime: Joi.number()
- .precision(3)
- .min(0)
- .max(100),
+ uptime: Joi.number().precision(3).min(0).max(100),
})
-const schema = Joi.array()
- .items(rowSchema)
- .min(1)
+const schema = Joi.array().items(rowSchema).min(1)
/*
* this is the checkUuid for the NodePing.com (as used on the [example page](https://nodeping.com/reporting.html#results))
@@ -28,35 +21,24 @@ const sampleCheckUuid = 'jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei'
// TODO: support for custom '100%' label
// TODO: support for custom # of decimal places
-module.exports = class NodePingUptime extends BaseJsonService {
- static get category() {
- return 'monitoring'
- }
+export default class NodePingUptime extends BaseJsonService {
+ static category = 'monitoring'
- static get route() {
- return {
- base: 'nodeping/uptime',
- pattern: ':checkUuid',
- }
- }
+ static route = { base: 'nodeping/uptime', pattern: ':checkUuid' }
- static get examples() {
- return [
- {
- title: 'NodePing uptime',
- namedParams: {
- checkUuid: sampleCheckUuid,
- },
- staticPreview: this.render({ uptime: 99.999 }),
+ static openApi = {
+ '/nodeping/uptime/{checkUuid}': {
+ get: {
+ summary: 'NodePing uptime',
+ parameters: pathParams({
+ name: 'checkUuid',
+ example: sampleCheckUuid,
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'Uptime',
- }
- }
+ static defaultBadgeData = { label: 'uptime' }
static formatPercentage(uptime) {
if (uptime === 100.0) {
@@ -74,7 +56,7 @@ module.exports = class NodePingUptime extends BaseJsonService {
async fetch({ checkUuid }) {
const thirtyDaysAgo = new Date(
- new Date().getTime() - 30 * 24 * 60 * 60 * 1000
+ new Date().getTime() - 30 * 24 * 60 * 60 * 1000,
)
.toISOString()
.slice(0, 10)
@@ -83,7 +65,11 @@ module.exports = class NodePingUptime extends BaseJsonService {
schema,
url: `https://nodeping.com/reports/uptime/${checkUuid}`,
options: {
- qs: { format: 'json', interval: 'days', start: thirtyDaysAgo },
+ searchParams: {
+ format: 'json',
+ interval: 'days',
+ start: thirtyDaysAgo,
+ },
headers: {
'cache-control': 'no-cache',
},
diff --git a/services/nodeping/nodeping-uptime.tester.js b/services/nodeping/nodeping-uptime.tester.js
index 504748dcaec06..fc3367336e249 100644
--- a/services/nodeping/nodeping-uptime.tester.js
+++ b/services/nodeping/nodeping-uptime.tester.js
@@ -1,14 +1,10 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isPercentage } = require('../test-validators')
+import { createServiceTester } from '../tester.js'
+import { isPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
t.create('NodePing uptime')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
- .expectBadge({
- label: 'Uptime',
- message: isPercentage,
- })
+ .expectBadge({ label: 'uptime', message: isPercentage })
t.create('NodePing uptime - 100%')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
@@ -16,18 +12,14 @@ t.create('NodePing uptime - 100%')
nock('https://nodeping.com')
.get(
`/reports/uptime/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei?format=json&interval=days&start=${new Date(
- new Date().getTime() - 30 * 24 * 60 * 60 * 1000
+ new Date().getTime() - 30 * 24 * 60 * 60 * 1000,
)
.toISOString()
- .slice(0, 10)}`
+ .slice(0, 10)}`,
)
- .reply(200, [{ uptime: 100 }])
+ .reply(200, [{ uptime: 100 }]),
)
- .expectBadge({
- label: 'Uptime',
- message: '100%',
- color: 'brightgreen',
- })
+ .expectBadge({ label: 'uptime', message: '100%', color: 'brightgreen' })
t.create('NodePing uptime - 99.999%')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
@@ -35,18 +27,14 @@ t.create('NodePing uptime - 99.999%')
nock('https://nodeping.com')
.get(
`/reports/uptime/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei?format=json&interval=days&start=${new Date(
- new Date().getTime() - 30 * 24 * 60 * 60 * 1000
+ new Date().getTime() - 30 * 24 * 60 * 60 * 1000,
)
.toISOString()
- .slice(0, 10)}`
+ .slice(0, 10)}`,
)
- .reply(200, [{ uptime: 99.999 }])
+ .reply(200, [{ uptime: 99.999 }]),
)
- .expectBadge({
- label: 'Uptime',
- message: '99.999%',
- color: 'green',
- })
+ .expectBadge({ label: 'uptime', message: '99.999%', color: 'green' })
t.create('NodePing uptime - 99.001%')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
@@ -54,18 +42,14 @@ t.create('NodePing uptime - 99.001%')
nock('https://nodeping.com')
.get(
`/reports/uptime/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei?format=json&interval=days&start=${new Date(
- new Date().getTime() - 30 * 24 * 60 * 60 * 1000
+ new Date().getTime() - 30 * 24 * 60 * 60 * 1000,
)
.toISOString()
- .slice(0, 10)}`
+ .slice(0, 10)}`,
)
- .reply(200, [{ uptime: 99.001 }])
+ .reply(200, [{ uptime: 99.001 }]),
)
- .expectBadge({
- label: 'Uptime',
- message: '99.001%',
- color: 'yellow',
- })
+ .expectBadge({ label: 'uptime', message: '99.001%', color: 'yellow' })
t.create('NodePing uptime - 90.001%')
.get('/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei.json')
@@ -73,15 +57,11 @@ t.create('NodePing uptime - 90.001%')
nock('https://nodeping.com')
.get(
`/reports/uptime/jkiwn052-ntpp-4lbb-8d45-ihew6d9ucoei?format=json&interval=days&start=${new Date(
- new Date().getTime() - 30 * 24 * 60 * 60 * 1000
+ new Date().getTime() - 30 * 24 * 60 * 60 * 1000,
)
.toISOString()
- .slice(0, 10)}`
+ .slice(0, 10)}`,
)
- .reply(200, [{ uptime: 90.001 }])
+ .reply(200, [{ uptime: 90.001 }]),
)
- .expectBadge({
- label: 'Uptime',
- message: '90.001%',
- color: 'red',
- })
+ .expectBadge({ label: 'uptime', message: '90.001%', color: 'red' })
diff --git a/services/nostr-band/nostr-band-followers.service.js b/services/nostr-band/nostr-band-followers.service.js
new file mode 100644
index 0000000000000..1758a03d30cd5
--- /dev/null
+++ b/services/nostr-band/nostr-band-followers.service.js
@@ -0,0 +1,11 @@
+import { retiredService } from '../index.js'
+
+export default retiredService({
+ category: 'social',
+ route: {
+ base: 'nostrband/followers',
+ pattern: ':npub',
+ },
+ label: 'nostrband',
+ dateAdded: new Date('2025-11-22'),
+})
diff --git a/services/nostr-band/nostr-band-followers.tester.js b/services/nostr-band/nostr-band-followers.tester.js
new file mode 100644
index 0000000000000..7d0bc0168280f
--- /dev/null
+++ b/services/nostr-band/nostr-band-followers.tester.js
@@ -0,0 +1,15 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'nostrband',
+ title: 'Nostr.band',
+})
+
+t.create('followers')
+ .get(
+ '/followers/npub18c556t7n8xa3df2q82rwxejfglw5przds7sqvefylzjh8tjne28qld0we7.json',
+ )
+ .expectBadge({
+ label: 'nostrband',
+ message: 'retired badge',
+ })
diff --git a/services/npm-stat/npm-stat-downloads.service.js b/services/npm-stat/npm-stat-downloads.service.js
new file mode 100644
index 0000000000000..d06a28ef3d4e6
--- /dev/null
+++ b/services/npm-stat/npm-stat-downloads.service.js
@@ -0,0 +1,82 @@
+import Joi from 'joi'
+import dayjs from 'dayjs'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+
+const schema = Joi.object()
+ .pattern(Joi.string(), Joi.object().pattern(Joi.string(), nonNegativeInteger))
+ .required()
+
+const intervalMap = {
+ dw: { interval: 'week' },
+ dm: { interval: 'month' },
+ dy: { interval: 'year' },
+}
+
+export default class NpmStatDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'npm-stat',
+ pattern: ':interval(dw|dm|dy)/:author',
+ }
+
+ static openApi = {
+ '/npm-stat/{interval}/{author}': {
+ get: {
+ summary: 'NPM Downloads by package author',
+ description:
+ 'The total number of downloads of npm packages published by the specified author from [npm-stat](https://npm-stat.com).',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dw',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Downloads per Week, Month or Year',
+ },
+ {
+ name: 'author',
+ example: 'dukeluo',
+ },
+ ),
+ },
+ },
+ }
+
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ static getTotalDownloads(data) {
+ const add = (x, y) => x + y
+ const sum = nums => nums.reduce(add, 0)
+
+ return Object.values(data).reduce(
+ (count, packageDownloads) => count + sum(Object.values(packageDownloads)),
+ 0,
+ )
+ }
+
+ static render({ interval, downloads }) {
+ return renderDownloadsBadge({
+ downloads,
+ interval: intervalMap[interval].interval,
+ colorOverride: downloads > 0 ? 'brightgreen' : 'red',
+ })
+ }
+
+ async handle({ interval, author }) {
+ const unit = intervalMap[interval].interval
+ const today = dayjs()
+ const until = today.format('YYYY-MM-DD')
+ const from = today.subtract(1, unit).format('YYYY-MM-DD')
+ const data = await this._requestJson({
+ url: `https://npm-stat.com/api/download-counts?author=${author}&from=${from}&until=${until}`,
+ schema,
+ })
+ const downloads = this.constructor.getTotalDownloads(data)
+
+ return this.constructor.render({ interval, downloads })
+ }
+}
diff --git a/services/npm-stat/npm-stat-downloads.spec.js b/services/npm-stat/npm-stat-downloads.spec.js
new file mode 100644
index 0000000000000..1932b9b650a7a
--- /dev/null
+++ b/services/npm-stat/npm-stat-downloads.spec.js
@@ -0,0 +1,25 @@
+import { test, given } from 'sazerac'
+import NpmStatDownloads from './npm-stat-downloads.service.js'
+
+describe('NpmStatDownloads helpers', function () {
+ test(NpmStatDownloads.getTotalDownloads, () => {
+ given({
+ 'hexo-theme-candelas': {
+ '2022-12-01': 1,
+ '2022-12-02': 2,
+ '2022-12-03': 3,
+ },
+ '@dukeluo/fanjs': {
+ '2022-12-01': 10,
+ '2022-12-02': 20,
+ '2022-12-03': 30,
+ },
+ 'eslint-plugin-check-file': {
+ '2022-12-01': 100,
+ '2022-12-02': 200,
+ '2022-12-03': 300,
+ },
+ }).expect(666)
+ given({}).expect(0)
+ })
+})
diff --git a/services/npm-stat/npm-stat-downloads.tester.js b/services/npm-stat/npm-stat-downloads.tester.js
new file mode 100644
index 0000000000000..830a4208f204c
--- /dev/null
+++ b/services/npm-stat/npm-stat-downloads.tester.js
@@ -0,0 +1,35 @@
+import { isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('weekly downloads of npm author dukeluo')
+ .get('/dw/dukeluo.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: 'brightgreen',
+ })
+
+t.create('monthly downloads of npm author dukeluo')
+ .get('/dm/dukeluo.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: 'brightgreen',
+ })
+
+t.create('yearly downloads of npm author dukeluo')
+ .get('/dy/dukeluo.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: 'brightgreen',
+ })
+
+t.create('downloads of unknown npm package author')
+ .get('/dy/npm-api-does-not-have-this-package-author.json')
+ .expectBadge({
+ label: 'downloads',
+ message: '0/year',
+ color: 'red',
+ })
diff --git a/services/npm/npm-base.js b/services/npm/npm-base.js
index 3ca60ccc52408..033e3b0709a0b 100644
--- a/services/npm/npm-base.js
+++ b/services/npm/npm-base.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { isDependencyMap } = require('../package-json-helpers')
-const { BaseJsonService, InvalidResponse, NotFound } = require('..')
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { isDependencyMap } from '../package-json-helpers.js'
+import { BaseJsonService, InvalidResponse, NotFound } from '../index.js'
const deprecatedLicenseObjectSchema = Joi.object({
type: Joi.string().required(),
@@ -17,39 +15,41 @@ const packageDataSchema = Joi.object({
Joi.string(),
deprecatedLicenseObjectSchema,
Joi.array().items(
- Joi.alternatives(Joi.string(), deprecatedLicenseObjectSchema)
- )
+ Joi.alternatives(Joi.string(), deprecatedLicenseObjectSchema),
+ ),
),
maintainers: Joi.array()
// We don't need the keys here, just the length.
.items(Joi.object({}))
- .required(),
+ .default([]),
types: Joi.string(),
// `typings` is an alias for `types` and often used
// https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#including-declarations-in-your-npm-package
// > Note that the "typings" field is synonymous with "types"
typings: Joi.string(),
- files: Joi.array()
- .items(Joi.string())
- .default([]),
+ files: Joi.array().items(Joi.string()).default([]),
}).required()
-const queryParamSchema = Joi.object({
+export const queryParamSchema = Joi.object({
registry_uri: optionalUrl,
}).required()
+export const packageNameDescription =
+ 'This may be the name of an unscoped package like `package-name` or a [scoped package](https://docs.npmjs.com/about-scopes) like `@author/package-name`'
+
// Abstract class for NPM badges which display data about the latest version
// of a package.
-module.exports = class NpmBase extends BaseJsonService {
- static get auth() {
- return { passKey: 'npm_token', serviceKey: 'npm' }
+export default class NpmBase extends BaseJsonService {
+ static auth = {
+ passKey: 'npm_token',
+ serviceKey: 'npm',
}
static buildRoute(base, { withTag } = {}) {
if (withTag) {
return {
base,
- pattern: ':scope(@[^/]+)?/:packageName/:tag?',
+ pattern: ':scope(@[^/]+)?/:packageName/:tag*',
queryParamSchema,
}
} else {
@@ -63,7 +63,7 @@ module.exports = class NpmBase extends BaseJsonService {
static unpackParams(
{ scope, packageName, tag },
- { registry_uri: registryUrl = 'https://registry.npmjs.org' }
+ { registry_uri: registryUrl = 'https://registry.npmjs.org' },
) {
return {
scope,
@@ -81,8 +81,11 @@ module.exports = class NpmBase extends BaseJsonService {
}
async _requestJson(data) {
- return super._requestJson(
- this.authHelper.withBearerAuthHeader({
+ let payload
+ if (data?.options?.headers?.Accept) {
+ payload = data
+ } else {
+ payload = {
...data,
options: {
headers: {
@@ -91,8 +94,9 @@ module.exports = class NpmBase extends BaseJsonService {
Accept: '*/*',
},
},
- })
- )
+ }
+ }
+ return super._requestJson(this.authHelper.withBearerAuthHeader(payload))
}
async fetchPackageData({ registryUrl, scope, packageName, tag }) {
@@ -120,7 +124,7 @@ module.exports = class NpmBase extends BaseJsonService {
// We don't validate here because we need to pluck the desired subkey first.
schema: Joi.any(),
url,
- errorMessages: { 404: 'package not found' },
+ httpErrors: { 404: 'package not found' },
})
let packageData
@@ -143,6 +147,37 @@ module.exports = class NpmBase extends BaseJsonService {
return this.constructor._validate(packageData, packageDataSchema)
}
-}
-module.exports.queryParamSchema = queryParamSchema
+ async fetch({
+ registryUrl,
+ scope,
+ packageName,
+ schema,
+ abbreviated = false,
+ }) {
+ registryUrl = registryUrl || this.constructor.defaultRegistryUrl
+ let url
+
+ if (scope === undefined) {
+ url = `${registryUrl}/${packageName}`
+ } else {
+ const scoped = this.constructor.encodeScopedPackage({
+ scope,
+ packageName,
+ })
+ url = `${registryUrl}/${scoped}`
+ }
+
+ // https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md
+ const options = abbreviated
+ ? { headers: { Accept: 'application/vnd.npm.install-v1+json' } }
+ : {}
+
+ return this._requestJson({
+ url,
+ schema,
+ options,
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+}
diff --git a/services/npm/npm-base.spec.js b/services/npm/npm-base.spec.js
index 45a0e3c8e55d4..b8a6bec47f007 100644
--- a/services/npm/npm-base.spec.js
+++ b/services/npm/npm-base.spec.js
@@ -1,14 +1,12 @@
-'use strict'
-
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
// use NPM Version as an example implementation of NpmBase for this test
-const NpmVersion = require('./npm-version.service')
+import NpmVersion from './npm-version.service.js'
-describe('npm', function() {
- describe('auth', function() {
- it('sends the auth information as configured', async function() {
+describe('npm', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
cleanUpNockAfterEach()
const token = 'abc123'
@@ -33,10 +31,10 @@ describe('npm', function() {
}
expect(
- await NpmVersion.invoke(defaultContext, config, { packageName: 'npm' })
+ await NpmVersion.invoke(defaultContext, config, { packageName: 'npm' }),
).to.deep.equal({
color: 'orange',
- label: undefined,
+ label: 'npm',
message: 'v0.1.0',
})
diff --git a/services/npm/npm-collaborators.service.js b/services/npm/npm-collaborators.service.js
index 36f0c98c1132d..dc386da9a68c4 100644
--- a/services/npm/npm-collaborators.service.js
+++ b/services/npm/npm-collaborators.service.js
@@ -1,43 +1,33 @@
-'use strict'
-
-const { renderContributorBadge } = require('../contributor-count')
-const NpmBase = require('./npm-base')
-
-const keywords = ['node']
-
-module.exports = class NpmCollaborators extends NpmBase {
- static get category() {
- return 'activity'
- }
-
- static get route() {
- return this.buildRoute('npm/collaborators', { withTag: false })
- }
-
- static get examples() {
- return [
- {
- title: 'npm collaborators',
- pattern: ':packageName',
- namedParams: { packageName: 'prettier' },
- staticPreview: this.render({ collaborators: 6 }),
- keywords,
- },
- {
- title: 'npm collaborators',
- pattern: ':packageName',
- namedParams: { packageName: 'prettier' },
- queryParams: { registry_uri: 'https://registry.npmjs.com' },
- staticPreview: this.render({ collaborators: 6 }),
- keywords,
+import { pathParam, queryParam } from '../index.js'
+import { renderContributorBadge } from '../contributor-count.js'
+import NpmBase, { packageNameDescription } from './npm-base.js'
+
+export default class NpmCollaborators extends NpmBase {
+ static category = 'activity'
+
+ static route = this.buildRoute('npm/collaborators', { withTag: false })
+
+ static openApi = {
+ '/npm/collaborators/{packageName}': {
+ get: {
+ summary: 'NPM Collaborators',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'prettier',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'npm collaborators',
- }
+ static defaultBadgeData = {
+ label: 'npm collaborators',
}
static render({ collaborators }) {
@@ -47,7 +37,7 @@ module.exports = class NpmCollaborators extends NpmBase {
async handle(namedParams, queryParams) {
const { scope, packageName, registryUrl } = this.constructor.unpackParams(
namedParams,
- queryParams
+ queryParams,
)
const { maintainers } = await this.fetchPackageData({
scope,
diff --git a/services/npm/npm-collaborators.tester.js b/services/npm/npm-collaborators.tester.js
index 45f6c9bab680a..c2f41b7e6bd42 100644
--- a/services/npm/npm-collaborators.tester.js
+++ b/services/npm/npm-collaborators.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { nonNegativeInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets the contributor count')
.get('/prettier.json')
@@ -17,3 +16,12 @@ t.create('contributor count for unknown package')
label: 'npm collaborators',
message: 'package not found',
})
+
+t.create('contributor count for package package without a maintainers property')
+ .get('/package-without-maintainers.json')
+ .intercept(nock =>
+ nock('https://registry.npmjs.org')
+ .get('/package-without-maintainers/latest')
+ .reply(200, {}),
+ )
+ .expectBadge({ label: 'npm collaborators', message: '0' })
diff --git a/services/npm/npm-dependency-version.service.js b/services/npm/npm-dependency-version.service.js
index 334e9e5a0c982..ffe25442b6e34 100644
--- a/services/npm/npm-dependency-version.service.js
+++ b/services/npm/npm-dependency-version.service.js
@@ -1,115 +1,72 @@
-'use strict'
+import { pathParam, queryParam } from '../index.js'
+import { getDependencyVersion } from '../package-json-helpers.js'
+import NpmBase, {
+ queryParamSchema,
+ packageNameDescription,
+} from './npm-base.js'
-const { getDependencyVersion } = require('../package-json-helpers')
-const NpmBase = require('./npm-base')
+export default class NpmDependencyVersion extends NpmBase {
+ static category = 'platform-support'
-const { queryParamSchema } = NpmBase
-const keywords = ['node']
-
-module.exports = class NpmDependencyVersion extends NpmBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return {
- base: 'npm/dependency-version',
- pattern:
- ':scope(@[^/]+)?/:packageName/:kind(dev|peer)?/:dependencyScope(@[^/]+)?/:dependency',
- queryParamSchema,
- }
+ static route = {
+ base: 'npm/dependency-version',
+ pattern:
+ ':scope(@[^/]+)?/:packageName/:kind(dev|peer)?/:dependencyScope(@[^/]+)?/:dependency',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'npm peer dependency version',
- pattern: ':packageName/peer/:dependency',
- namedParams: {
- packageName: 'react-boxplot',
- dependency: 'prop-types',
- },
- staticPreview: this.render({
- dependency: 'prop-types',
- range: '^15.5.4',
- }),
- keywords,
- },
- {
- title: 'npm peer dependency version (scoped)',
- pattern: ':scope?/:packageName/dev/:dependencyScope?/:dependency',
- namedParams: {
- scope: '@swellaby',
- packageName: 'eslint-config',
- dependency: 'eslint',
- },
- staticPreview: this.render({
- dependency: 'eslint',
- range: '^3.0.0',
- }),
- keywords,
- },
- {
- title: 'npm dev dependency version',
- pattern: ':packageName/dev/:dependency',
- namedParams: {
- packageName: 'react-boxplot',
- dependency: 'eslint-config-standard',
- },
- staticPreview: this.render({
- dependency: 'eslint-config-standard',
- range: '^12.0.0',
- }),
- keywords,
- },
- {
- title: 'npm dev dependency version (scoped)',
- pattern: ':scope?/:packageName/dev/:dependencyScope?/:dependency',
- namedParams: {
- packageName: 'mocha',
- dependencyScope: '@mocha',
- dependency: 'contributors',
- },
- staticPreview: this.render({
- dependency: '@mocha/contributors',
- range: '^1.0.3',
- }),
- keywords,
+ static openApi = {
+ '/npm/dependency-version/{packageName}/{dependency}': {
+ get: {
+ summary: 'NPM (prod) Dependency Version',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'react-boxplot',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'dependency',
+ example: 'simple-statistics',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- {
- title: 'npm (prod) dependency version',
- pattern: ':packageName/:dependency',
- namedParams: {
- packageName: 'react-boxplot',
- dependency: 'simple-statistics',
- },
- staticPreview: this.render({
- dependency: 'simple-statistics',
- range: '^6.1.1',
- }),
- keywords,
+ },
+ '/npm/dependency-version/{packageName}/{kind}/{dependency}': {
+ get: {
+ summary: 'NPM dev or peer Dependency Version',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'react-boxplot',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'kind',
+ example: 'dev',
+ schema: { type: 'string', enum: this.getEnum('kind') },
+ }),
+ pathParam({
+ name: 'dependency',
+ example: 'prop-types',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- {
- title: 'npm (prod) dependency version (scoped)',
- pattern: ':scope?/:packageName/:dependencyScope?/:dependency',
- namedParams: {
- packageName: 'got',
- dependencyScope: '@sindresorhus',
- dependency: 'is',
- },
- staticPreview: this.render({
- dependency: '@sindresorhus/is',
- range: '^0.15.0',
- }),
- keywords,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'dependency',
- }
+ static defaultBadgeData = {
+ label: 'dependency',
}
static render({ dependency, range }) {
@@ -123,24 +80,21 @@ module.exports = class NpmDependencyVersion extends NpmBase {
async handle(namedParams, queryParams) {
const { scope, packageName, registryUrl } = this.constructor.unpackParams(
namedParams,
- queryParams
+ queryParams,
)
const { kind, dependency, dependencyScope } = namedParams
const wantedDependency = `${
dependencyScope ? `${dependencyScope}/` : ''
}${dependency}`
- const {
- dependencies,
- devDependencies,
- peerDependencies,
- } = await this.fetchPackageData({
- scope,
- packageName,
- registryUrl,
- })
+ const { dependencies, devDependencies, peerDependencies } =
+ await this.fetchPackageData({
+ scope,
+ packageName,
+ registryUrl,
+ })
- const { range } = getDependencyVersion({
+ const range = getDependencyVersion({
kind,
wantedDependency,
dependencies,
diff --git a/services/npm/npm-dependency-version.tester.js b/services/npm/npm-dependency-version.tester.js
index f72f598b1ed3a..864c4d6b15e7f 100644
--- a/services/npm/npm-dependency-version.tester.js
+++ b/services/npm/npm-dependency-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { semverRange } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { semverRange } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets the peer dependency version')
.get('/react-boxplot/peer/react.json')
diff --git a/services/npm/npm-downloads-redirect.service.js b/services/npm/npm-downloads-redirect.service.js
new file mode 100644
index 0000000000000..9227d8a6dd9aa
--- /dev/null
+++ b/services/npm/npm-downloads-redirect.service.js
@@ -0,0 +1,11 @@
+import { redirector } from '../index.js'
+
+export default redirector({
+ category: 'downloads',
+ route: {
+ base: 'npm/dt',
+ pattern: ':packageName+',
+ },
+ transformPath: ({ packageName }) => `/npm/d18m/${packageName}`,
+ dateAdded: new Date('2024-03-19'),
+})
diff --git a/services/npm/npm-downloads.service.js b/services/npm/npm-downloads.service.js
index 7620b8c96efb3..67a42c8711483 100644
--- a/services/npm/npm-downloads.service.js
+++ b/services/npm/npm-downloads.service.js
@@ -1,9 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { packageNameDescription } from './npm-base.js'
// https://github.com/npm/registry/blob/master/docs/download-counts.md#output
const pointResponseSchema = Joi.object({
@@ -15,73 +14,76 @@ const intervalMap = {
query: 'point/last-week',
schema: pointResponseSchema,
transform: json => json.downloads,
- messageSuffix: '/week',
+ interval: 'week',
},
dm: {
query: 'point/last-month',
schema: pointResponseSchema,
transform: json => json.downloads,
- messageSuffix: '/month',
+ interval: 'month',
},
dy: {
query: 'point/last-year',
schema: pointResponseSchema,
transform: json => json.downloads,
- messageSuffix: '/year',
+ interval: 'year',
},
- dt: {
+ d18m: {
query: 'range/1000-01-01:3000-01-01',
// https://github.com/npm/registry/blob/master/docs/download-counts.md#output-1
schema: Joi.object({
- downloads: Joi.array()
- .items(pointResponseSchema)
- .required(),
+ downloads: Joi.array().items(pointResponseSchema).required(),
}).required(),
transform: json =>
json.downloads
.map(item => item.downloads)
.reduce((accum, current) => accum + current),
- messageSuffix: '',
},
}
// This hits an entirely different API from the rest of the NPM services, so
// it does not use NpmBase.
-module.exports = class NpmDownloads extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
+export default class NpmDownloads extends BaseJsonService {
+ static category = 'downloads'
- static get route() {
- return {
- base: 'npm',
- pattern: ':interval(dw|dm|dy|dt)/:scope(@.+)?/:packageName',
- }
+ static route = {
+ base: 'npm',
+ pattern: ':interval(dw|dm|dy|d18m)/:scope(@.+)?/:packageName',
}
- static get examples() {
- return [
- {
- title: 'npm',
- namedParams: { interval: 'dw', packageName: 'localeval' },
- staticPreview: this.render({ interval: 'dw', downloadCount: 30000 }),
- keywords: ['node'],
+ static openApi = {
+ '/npm/{interval}/{packageName}': {
+ get: {
+ summary: 'NPM Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dw',
+ description:
+ 'Downloads in the last Week, Month, Year, or 18 Months',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ },
+ {
+ name: 'packageName',
+ example: 'localeval',
+ description: packageNameDescription,
+ },
+ ),
},
- ]
+ },
}
- // For testing.
- static get _intervalMap() {
- return intervalMap
- }
+ static _cacheLength = 7200
- static render({ interval, downloadCount }) {
- const { messageSuffix } = intervalMap[interval]
+ // For testing.
+ static _intervalMap = intervalMap
- return {
- message: `${metric(downloadCount)}${messageSuffix}`,
- color: downloadCount > 0 ? 'brightgreen' : 'red',
- }
+ static render({ interval, downloadCount: downloads }) {
+ return renderDownloadsBadge({
+ downloads,
+ interval: intervalMap[interval].interval,
+ colorOverride: downloads > 0 ? 'brightgreen' : 'red',
+ })
}
async handle({ interval, scope, packageName }) {
@@ -91,7 +93,7 @@ module.exports = class NpmDownloads extends BaseJsonService {
const json = await this._requestJson({
schema,
url: `https://api.npmjs.org/downloads/${query}/${slug}`,
- errorMessages: { 404: 'package not found or too new' },
+ httpErrors: { 404: 'package not found or too new' },
})
const downloadCount = transform(json)
diff --git a/services/npm/npm-downloads.spec.js b/services/npm/npm-downloads.spec.js
index 0e610057f166c..6529dd57efadf 100644
--- a/services/npm/npm-downloads.spec.js
+++ b/services/npm/npm-downloads.spec.js
@@ -1,10 +1,8 @@
-'use strict'
+import { test, given } from 'sazerac'
+import NpmDownloads from './npm-downloads.service.js'
-const { test, given } = require('sazerac')
-const NpmDownloads = require('./npm-downloads.service')
-
-describe('NpmDownloads', function() {
- test(NpmDownloads._intervalMap.dt.transform, () => {
+describe('NpmDownloads', function () {
+ test(NpmDownloads._intervalMap.d18m.transform, () => {
given({
downloads: [
{ downloads: 2, day: '2018-01-01' },
@@ -15,11 +13,12 @@ describe('NpmDownloads', function() {
test(NpmDownloads.render, () => {
given({
- interval: 'dt',
+ interval: 'd18m',
downloadCount: 0,
}).expect({
- message: '0',
color: 'red',
+ message: '0',
+ label: undefined,
})
})
})
diff --git a/services/npm/npm-downloads.tester.js b/services/npm/npm-downloads.tester.js
index b7ac2bf82ce3a..7c02c940e9e82 100644
--- a/services/npm/npm-downloads.tester.js
+++ b/services/npm/npm-downloads.tester.js
@@ -1,36 +1,41 @@
-'use strict'
+import { isMetricOverTimePeriod, isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetricOverTimePeriod, isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('weekly downloads of left-pad')
- .get('/dw/left-pad.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- color: 'brightgreen',
- })
+t.create('weekly downloads of left-pad').get('/dw/left-pad.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: 'brightgreen',
+})
t.create('weekly downloads of @cycle/core')
.get('/dw/@cycle/core.json')
.expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
-t.create('total downloads of left-pad')
- .get('/dt/left-pad.json')
+t.create('downloads in last 18 months of left-pad')
+ .get('/d18m/left-pad.json')
.expectBadge({
label: 'downloads',
message: isMetric,
color: 'brightgreen',
})
-t.create('total downloads of @cycle/core')
- .get('/dt/@cycle/core.json')
+t.create('downloads in last 18 months of @cycle/core')
+ .get('/d18m/@cycle/core.json')
.expectBadge({ label: 'downloads', message: isMetric })
t.create('downloads of unknown package')
- .get('/dt/npm-api-does-not-have-this-package.json')
+ .get('/dy/npm-api-does-not-have-this-package.json')
.expectBadge({
label: 'downloads',
message: 'package not found or too new',
color: 'red',
})
+
+t.create('Total downloads redirect: unscoped package')
+ .get('/dt/left-pad.svg')
+ .expectRedirect('/npm/d18m/left-pad.svg')
+
+t.create('Total downloads redirect: scoped package')
+ .get('/dt/@cycle/core.svg')
+ .expectRedirect('/npm/d18m/@cycle/core.svg')
diff --git a/services/npm/npm-last-update.service.js b/services/npm/npm-last-update.service.js
new file mode 100644
index 0000000000000..b8d254f5ef7a1
--- /dev/null
+++ b/services/npm/npm-last-update.service.js
@@ -0,0 +1,119 @@
+import Joi from 'joi'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import NpmBase, {
+ packageNameDescription,
+ queryParamSchema,
+} from './npm-base.js'
+
+const fullSchema = Joi.object({
+ time: Joi.object()
+ .pattern(Joi.string().required(), Joi.string().required())
+ .required(),
+ 'dist-tags': Joi.object()
+ .pattern(Joi.string().required(), Joi.string().required())
+ .required(),
+}).required()
+
+const abbreviatedSchema = Joi.object({
+ modified: Joi.string().required(),
+}).required()
+
+export class NpmLastUpdateWithTag extends NpmBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'npm/last-update',
+ pattern: ':scope(@[^/]+)?/:packageName/:tag',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/npm/last-update/{packageName}/{tag}': {
+ get: {
+ summary: 'NPM Last Update (with dist tag)',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'verdaccio',
+ packageNameDescription,
+ }),
+ pathParam({
+ name: 'tag',
+ example: 'next-8',
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ async handle(namedParams, queryParams) {
+ const { scope, packageName, tag, registryUrl } =
+ this.constructor.unpackParams(namedParams, queryParams)
+
+ const packageData = await this.fetch({
+ registryUrl,
+ scope,
+ packageName,
+ schema: fullSchema,
+ })
+
+ const tagVersion = packageData['dist-tags'][tag]
+
+ if (!tagVersion) {
+ throw new NotFound({ prettyMessage: 'tag not found' })
+ }
+
+ return renderDateBadge(packageData.time[tagVersion])
+ }
+}
+
+export class NpmLastUpdate extends NpmBase {
+ static category = 'activity'
+
+ static route = this.buildRoute('npm/last-update', { withTag: false })
+
+ static openApi = {
+ '/npm/last-update/{packageName}': {
+ get: {
+ summary: 'NPM Last Update',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'verdaccio',
+ packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ async handle(namedParams, queryParams) {
+ const { scope, packageName, registryUrl } = this.constructor.unpackParams(
+ namedParams,
+ queryParams,
+ )
+
+ const packageData = await this.fetch({
+ registryUrl,
+ scope,
+ packageName,
+ schema: abbreviatedSchema,
+ abbreviated: true,
+ })
+
+ return renderDateBadge(packageData.modified)
+ }
+}
diff --git a/services/npm/npm-last-update.tester.js b/services/npm/npm-last-update.tester.js
new file mode 100644
index 0000000000000..7fcead5b5f534
--- /dev/null
+++ b/services/npm/npm-last-update.tester.js
@@ -0,0 +1,81 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('last updated date, no tag, valid package')
+ .get('/verdaccio.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, no tag, invalid package')
+ .get('/not-a-package.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'package not found',
+ })
+
+t.create('last updated date, no tag, custom repository, valid package')
+ .get('/verdaccio.json?registry_uri=https://registry.npmjs.com')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, no tag, valid package with scope')
+ .get('/@npm/types.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, no tag, invalid package with scope')
+ .get('/@not-a-scoped-package/not-a-valid-package.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'package not found',
+ })
+
+t.create('last updated date, with tag, valid package')
+ .get('/verdaccio/latest.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, with tag, invalid package')
+ .get('/not-a-package/doesnt-matter.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'package not found',
+ })
+
+t.create('last updated date, with tag, invalid tag')
+ .get('/verdaccio/not-a-valid-tag.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'tag not found',
+ })
+
+t.create('last updated date, with tag, custom repository, valid package')
+ .get('/verdaccio/latest.json?registry_uri=https://registry.npmjs.com')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, with tag, valid package with scope')
+ .get('/@npm/types/latest.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last updated date, with tag, invalid package with scope')
+ .get('/@not-a-scoped-package/not-a-valid-package/doesnt-matter.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'package not found',
+ })
diff --git a/services/npm/npm-license.service.js b/services/npm/npm-license.service.js
index 3c19609c1e93b..795e6a2ec1dd2 100644
--- a/services/npm/npm-license.service.js
+++ b/services/npm/npm-license.service.js
@@ -1,36 +1,30 @@
-'use strict'
+import { pathParam, queryParam } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import toArray from '../../core/base-service/to-array.js'
+import NpmBase, { packageNameDescription } from './npm-base.js'
-const { renderLicenseBadge } = require('../licenses')
-const toArray = require('../../core/base-service/to-array')
-const NpmBase = require('./npm-base')
+export default class NpmLicense extends NpmBase {
+ static category = 'license'
-module.exports = class NpmLicense extends NpmBase {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return this.buildRoute('npm/l', { withTag: false })
- }
+ static route = this.buildRoute('npm/l', { withTag: false })
- static get examples() {
- return [
- {
- title: 'NPM',
- pattern: ':packageName',
- namedParams: { packageName: 'express' },
- staticPreview: this.render({ licenses: ['MIT'] }),
- keywords: ['node'],
+ static openApi = {
+ '/npm/l/{packageName}': {
+ get: {
+ summary: 'NPM License',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'express',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- {
- title: 'NPM',
- pattern: ':packageName',
- namedParams: { packageName: 'express' },
- queryParams: { registry_uri: 'https://registry.npmjs.com' },
- staticPreview: this.render({ licenses: ['MIT'] }),
- keywords: ['node'],
- },
- ]
+ },
}
static render({ licenses }) {
@@ -40,16 +34,19 @@ module.exports = class NpmLicense extends NpmBase {
async handle(namedParams, queryParams) {
const { scope, packageName, registryUrl } = this.constructor.unpackParams(
namedParams,
- queryParams
+ queryParams,
)
const { license } = await this.fetchPackageData({
scope,
packageName,
registryUrl,
})
- const licenses = toArray(license).map(license =>
- typeof license === 'string' ? license : license.type
- )
+ const licenses = toArray(license)
+ .map(license => (typeof license === 'string' ? license : license.type))
+ .flatMap(license => {
+ const match = license.match(/^\((.+)\)$/)
+ return match ? match[1].split(/\s+(?:OR|AND)\s+/) : [license]
+ })
return this.constructor.render({ licenses })
}
}
diff --git a/services/npm/npm-license.tester.js b/services/npm/npm-license.tester.js
index d30c5783b34b3..8e14be5987190 100644
--- a/services/npm/npm-license.tester.js
+++ b/services/npm/npm-license.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets the license of express')
.get('/express.json')
@@ -27,13 +26,13 @@ t.create('permissive license for scoped package')
.expectBadge({ label: 'license', message: 'MIT', color: 'green' })
t.create(
- 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)'
+ 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)',
)
.get('/rho-cc-promise.json')
.expectBadge({
label: 'license',
- message: '(MPL-2.0 OR MIT)',
- color: 'lightgrey',
+ message: 'MPL-2.0, MIT',
+ color: 'green',
})
t.create('license for package without a license property')
@@ -44,7 +43,7 @@ t.create('license for package without a license property')
.reply(200, {
label: 'package-without-license',
maintainers: [],
- })
+ }),
)
.expectBadge({ label: 'license', message: 'missing', color: 'red' })
@@ -60,7 +59,7 @@ t.create('license for package with a license object')
url: 'https://www.opensource.org/licenses/mit-license.php',
},
maintainers: [],
- })
+ }),
)
.expectBadge({ label: 'license', message: 'MIT', color: 'green' })
@@ -73,7 +72,7 @@ t.create('license for package with a license array')
label: 'package-license-object',
license: ['MPL-2.0', 'MIT'],
maintainers: [],
- })
+ }),
)
.expectBadge({
label: 'license',
@@ -100,7 +99,7 @@ t.create('when json is malformed for scoped package')
latest: '1.2.3',
},
versions: null,
- })
+ }),
)
.expectBadge({
label: 'license',
diff --git a/services/npm/npm-type-definitions.service.js b/services/npm/npm-type-definitions.service.js
index 46d8bf9cb087d..01bf009654c77 100644
--- a/services/npm/npm-type-definitions.service.js
+++ b/services/npm/npm-type-definitions.service.js
@@ -1,35 +1,35 @@
-'use strict'
-
-const NpmBase = require('./npm-base')
+import { pathParam, queryParam } from '../index.js'
+import NpmBase, { packageNameDescription } from './npm-base.js'
// For this badge to correctly detect type definitions, either the relevant
// dependencies must be declared, or the `types` key must be set in
// package.json.
-module.exports = class NpmTypeDefinitions extends NpmBase {
- static get category() {
- return 'platform-support'
- }
+export default class NpmTypeDefinitions extends NpmBase {
+ static category = 'platform-support'
- static get route() {
- return this.buildRoute('npm/types', { withTag: false })
- }
+ static route = this.buildRoute('npm/types', { withTag: false })
- static get examples() {
- return [
- {
- title: 'npm type definitions',
- pattern: ':packageName',
- namedParams: { packageName: 'chalk' },
- staticPreview: this.render({
- supportedLanguages: ['TypeScript', 'Flow'],
- }),
- keywords: ['node', 'typescript', 'flow'],
+ static openApi = {
+ '/npm/types/{packageName}': {
+ get: {
+ summary: 'NPM Type Definitions',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'chalk',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'types' }
+ static defaultBadgeData = {
+ label: 'types',
}
static render({ supportedLanguages }) {
@@ -68,7 +68,7 @@ module.exports = class NpmTypeDefinitions extends NpmBase {
async handle(namedParams, queryParams) {
const { scope, packageName, registryUrl } = this.constructor.unpackParams(
namedParams,
- queryParams
+ queryParams,
)
const json = await this.fetchPackageData({
scope,
diff --git a/services/npm/npm-type-definitions.spec.js b/services/npm/npm-type-definitions.spec.js
index f72b0d098cba3..69c4f3d45af9f 100644
--- a/services/npm/npm-type-definitions.spec.js
+++ b/services/npm/npm-type-definitions.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given, forCases } from 'sazerac'
+import NpmTypeDefinitions from './npm-type-definitions.service.js'
-const { test, given, forCases } = require('sazerac')
-const NpmTypeDefinitions = require('./npm-type-definitions.service')
-
-describe('NPM type definitions badge', function() {
+describe('NPM type definitions badge', function () {
test(NpmTypeDefinitions.transform, () => {
forCases([
given({ devDependencies: { typescript: '^2.4.7' }, files: [] }),
diff --git a/services/npm/npm-type-definitions.tester.js b/services/npm/npm-type-definitions.tester.js
index f49a5ba077040..0029bdb36d9aa 100644
--- a/services/npm/npm-type-definitions.tester.js
+++ b/services/npm/npm-type-definitions.tester.js
@@ -1,10 +1,9 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isTypeDefinition = Joi.string().regex(
- /^((Flow|TypeScript)|(Flow \| TypeScript))$/
+ /^((Flow|TypeScript)|(Flow \| TypeScript))$/,
)
t.create('types (from dev dependencies)')
@@ -15,11 +14,11 @@ t.create('types (from files)')
.get('/form-data-entries.json')
.intercept(nock =>
nock('https://registry.npmjs.org')
- .get(`/form-data-entries/latest`)
+ .get('/form-data-entries/latest')
.reply(200, {
maintainers: [],
files: ['index.js', 'index.d.ts'],
- })
+ }),
)
.expectBadge({ label: 'types', message: isTypeDefinition })
diff --git a/services/npm/npm-unpacked-size.service.js b/services/npm/npm-unpacked-size.service.js
new file mode 100644
index 0000000000000..e187234db9220
--- /dev/null
+++ b/services/npm/npm-unpacked-size.service.js
@@ -0,0 +1,94 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { renderSizeBadge } from '../size.js'
+import { optionalNonNegativeInteger } from '../validators.js'
+import NpmBase, {
+ packageNameDescription,
+ queryParamSchema,
+} from './npm-base.js'
+
+const schema = Joi.object({
+ dist: Joi.object({
+ unpackedSize: optionalNonNegativeInteger,
+ }).required(),
+}).required()
+
+export default class NpmUnpackedSize extends NpmBase {
+ static category = 'size'
+
+ static route = {
+ base: 'npm/unpacked-size',
+ pattern: ':scope(@[^/]+)?/:packageName/:version*',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/npm/unpacked-size/{packageName}': {
+ get: {
+ summary: 'NPM Unpacked Size',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'npm',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ '/npm/unpacked-size/{packageName}/{version}': {
+ get: {
+ summary: 'NPM Unpacked Size (with version)',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'npm',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'version',
+ example: '4.18.2',
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'unpacked size' }
+
+ async fetch({ registryUrl, packageName, version }) {
+ return this._requestJson({
+ schema,
+ url: `${registryUrl}/${packageName}/${version}`,
+ })
+ }
+
+ async handle(
+ { scope, packageName, version },
+ { registry_uri: registryUrl = 'https://registry.npmjs.org' },
+ ) {
+ const packageNameWithScope = scope ? `${scope}/${packageName}` : packageName
+ const { dist } = await this.fetch({
+ registryUrl,
+ packageName: packageNameWithScope,
+ version: version ?? 'latest',
+ })
+ const { unpackedSize } = dist
+
+ if (unpackedSize) {
+ return renderSizeBadge(unpackedSize, 'metric', 'unpacked size')
+ }
+ return {
+ label: 'unpacked size',
+ message: 'unknown',
+ color: 'lightgray',
+ }
+ }
+}
diff --git a/services/npm/npm-unpacked-size.tester.js b/services/npm/npm-unpacked-size.tester.js
new file mode 100644
index 0000000000000..436310d1aaba2
--- /dev/null
+++ b/services/npm/npm-unpacked-size.tester.js
@@ -0,0 +1,28 @@
+import { isMetricFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('Latest unpacked size')
+ .get('/firereact.json')
+ .expectBadge({ label: 'unpacked size', message: isMetricFileSize })
+
+t.create('Nonexistent unpacked size with version')
+ .get('/express/4.16.0.json')
+ .expectBadge({ label: 'unpacked size', message: 'unknown' })
+
+t.create('Unpacked size with version')
+ .get('/firereact/0.7.0.json')
+ .expectBadge({ label: 'unpacked size', message: '147.2 kB' })
+
+t.create('Unpacked size for scoped package')
+ .get('/@testing-library/react.json')
+ .expectBadge({ label: 'unpacked size', message: isMetricFileSize })
+
+t.create('Unpacked size for scoped package with version')
+ .get('/@testing-library/react/14.2.1.json')
+ .expectBadge({ label: 'unpacked size', message: '5.4 MB' })
+
+t.create('Nonexistent unpacked size for scoped package with version')
+ .get('/@cycle/rx-run/7.2.0.json')
+ .expectBadge({ label: 'unpacked size', message: 'unknown' })
diff --git a/services/npm/npm-version.service.js b/services/npm/npm-version.service.js
index ae0a5ece1a227..4843d46cb7a24 100644
--- a/services/npm/npm-version.service.js
+++ b/services/npm/npm-version.service.js
@@ -1,11 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { NotFound } = require('..')
-const NpmBase = require('./npm-base')
-
-const keywords = ['node']
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import NpmBase, { packageNameDescription } from './npm-base.js'
// Joi.string should be a semver.
const schema = Joi.object()
@@ -13,58 +9,52 @@ const schema = Joi.object()
.pattern(/./, Joi.string())
.required()
-module.exports = class NpmVersion extends NpmBase {
- static get category() {
- return 'version'
- }
+export default class NpmVersion extends NpmBase {
+ static category = 'version'
- static get route() {
- return this.buildRoute('npm/v', { withTag: true })
- }
+ static route = this.buildRoute('npm/v', { withTag: true })
- static get examples() {
- return [
- {
- title: 'npm',
- pattern: ':packageName',
- namedParams: { packageName: 'npm' },
- staticPreview: this.render({ version: '6.3.0' }),
- keywords,
+ static openApi = {
+ '/npm/v/{packageName}': {
+ get: {
+ summary: 'NPM Version',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'npm',
+ description: packageNameDescription,
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- {
- title: 'npm (scoped)',
- pattern: ':scope/:packageName',
- namedParams: { scope: '@cycle', packageName: 'core' },
- staticPreview: this.render({ version: '7.0.0' }),
- keywords,
+ },
+ '/npm/v/{packageName}/{tag}': {
+ get: {
+ summary: 'NPM Version (with dist tag)',
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'npm',
+ description: packageNameDescription,
+ }),
+ pathParam({
+ name: 'tag',
+ example: 'next-8',
+ }),
+ queryParam({
+ name: 'registry_uri',
+ example: 'https://registry.npmjs.com',
+ }),
+ ],
},
- {
- title: 'npm (tag)',
- pattern: ':packageName/:tag',
- namedParams: { packageName: 'npm', tag: 'next' },
- staticPreview: this.render({ tag: 'latest', version: '6.3.0' }),
- keywords,
- },
- {
- title: 'npm (custom registry)',
- pattern: ':packageName/:tag',
- namedParams: { packageName: 'npm', tag: 'next' },
- queryParams: { registry_uri: 'https://registry.npmjs.com' },
- staticPreview: this.render({ tag: 'latest', version: '7.0.0' }),
- keywords,
- },
- {
- title: 'npm (scoped with tag)',
- pattern: ':scope/:packageName/:tag',
- namedParams: { scope: '@cycle', packageName: 'core', tag: 'canary' },
- staticPreview: this.render({ tag: 'latest', version: '6.3.0' }),
- keywords,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'npm' }
+ static defaultBadgeData = {
+ label: 'npm',
}
static render({ tag, version }) {
@@ -77,12 +67,8 @@ module.exports = class NpmVersion extends NpmBase {
}
async handle(namedParams, queryParams) {
- const {
- scope,
- packageName,
- tag,
- registryUrl,
- } = this.constructor.unpackParams(namedParams, queryParams)
+ const { scope, packageName, tag, registryUrl } =
+ this.constructor.unpackParams(namedParams, queryParams)
const slug =
scope === undefined
@@ -92,7 +78,7 @@ module.exports = class NpmVersion extends NpmBase {
const packageData = await this._requestJson({
schema,
url: `${registryUrl}/-/package/${slug}/dist-tags`,
- errorMessages: { 404: 'package not found' },
+ httpErrors: { 404: 'package not found' },
})
if (tag && !(tag in packageData)) {
diff --git a/services/npm/npm-version.tester.js b/services/npm/npm-version.tester.js
index 0b6f0864311ed..0a2683ff982fe 100644
--- a/services/npm/npm-version.tester.js
+++ b/services/npm/npm-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets the package version of left-pad')
.get('/left-pad.json')
@@ -16,14 +15,14 @@ t.create('gets the package version of @cycle/core')
.expectBadge({ label: 'npm', message: isSemver })
t.create('gets a tagged package version of npm')
- .get('/npm/next.json')
- .expectBadge({ label: 'npm@next', message: isSemver })
+ .get('/npm/next-8.json')
+ .expectBadge({ label: 'npm@next-8', message: isSemver })
t.create('gets the correct tagged package version of npm')
.intercept(nock =>
nock('https://registry.npmjs.org')
.get('/-/package/npm/dist-tags')
- .reply(200, { latest: '1.2.3', next: '4.5.6' })
+ .reply(200, { latest: '1.2.3', next: '4.5.6' }),
)
.get('/npm/next.json')
.expectBadge({ label: 'npm@next', message: 'v4.5.6' })
@@ -36,12 +35,21 @@ t.create('gets the package version of left-pad from a custom registry')
.get('/left-pad.json?registry_uri=https://registry.npmjs.com')
.expectBadge({ label: 'npm', message: isSemver })
+t.create('gets the tagged package version with a "/" in the tag name')
+ .intercept(nock =>
+ nock('https://registry.npmjs.org')
+ .get('/-/package/npm/dist-tags')
+ .reply(200, { 'release/1.0': '1.0.3', latest: '2.0.1' }),
+ )
+ .get('/npm/release/1.0.json')
+ .expectBadge({ label: 'npm@release/1.0', message: 'v1.0.3' })
+
t.create('gets the tagged package version of @cycle/core')
.get('/@cycle/core/canary.json')
.expectBadge({ label: 'npm@canary', message: isSemver })
t.create(
- 'gets the tagged package version of @cycle/core from a custom registry'
+ 'gets the tagged package version of @cycle/core from a custom registry',
)
.get('/@cycle/core/canary.json?registry_uri=https://registry.npmjs.com')
.expectBadge({ label: 'npm@canary', message: isSemver })
@@ -54,7 +62,7 @@ t.create("Response doesn't include a 'latest' key")
.intercept(nock =>
nock('https://registry.npmjs.org')
.get('/-/package/npm/dist-tags')
- .reply(200, { next: 'v4.5.6' })
+ .reply(200, { next: 'v4.5.6' }),
)
.get('/npm.json')
.expectBadge({
diff --git a/services/npms-io/npms-io-score.service.js b/services/npms-io/npms-io-score.service.js
new file mode 100644
index 0000000000000..52f09c33f0696
--- /dev/null
+++ b/services/npms-io/npms-io-score.service.js
@@ -0,0 +1,99 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { coveragePercentage } from '../color-formatters.js'
+
+// https://api-docs.npms.io/#api-Package-GetPackageInfo
+const numberSchema = Joi.number().min(0).max(1).required()
+const responseSchema = Joi.object({
+ score: Joi.object({
+ final: numberSchema,
+ detail: Joi.object({
+ maintenance: numberSchema,
+ popularity: numberSchema,
+ quality: numberSchema,
+ }),
+ }),
+}).required()
+
+const description =
+ '[npms.io](https://npms.io) holds statistics for javascript packages.'
+
+export default class NpmsIOScore extends BaseJsonService {
+ static category = 'analysis'
+
+ static route = {
+ base: 'npms-io',
+ pattern:
+ ':type(final-score|maintenance-score|popularity-score|quality-score)/:scope(@.+)?/:packageName',
+ }
+
+ static openApi = {
+ '/npms-io/{type}/{packageName}': {
+ get: {
+ summary: 'npms.io',
+ description,
+ parameters: pathParams(
+ {
+ name: 'type',
+ schema: { type: 'string', enum: this.getEnum('type') },
+ example: 'maintenance-score',
+ },
+ {
+ name: 'packageName',
+ example: 'command',
+ },
+ ),
+ },
+ },
+ '/npms-io/{type}/{scope}/{packageName}': {
+ get: {
+ summary: 'npms.io (scoped package)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'type',
+ schema: { type: 'string', enum: this.getEnum('type') },
+ example: 'maintenance-score',
+ },
+ {
+ name: 'scope',
+ example: '@vue',
+ },
+ {
+ name: 'packageName',
+ example: 'cli',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'score',
+ }
+
+ static render({ type, score }) {
+ return {
+ label: type === 'final' ? 'score' : type,
+ message: `${(score * 100).toFixed(0)}%`,
+ color: coveragePercentage(score * 100),
+ }
+ }
+
+ async handle({ type, scope, packageName }) {
+ const slug = scope ? `${scope}/${packageName}` : packageName
+ const url = `https://api.npms.io/v2/package/${encodeURIComponent(slug)}`
+
+ const json = await this._requestJson({
+ schema: responseSchema,
+ url,
+ httpErrors: { 404: 'package not found or too new' },
+ })
+
+ const scoreType = type.slice(0, -6)
+ const score =
+ scoreType === 'final' ? json.score.final : json.score.detail[scoreType]
+
+ return this.constructor.render({ type: scoreType, score })
+ }
+}
diff --git a/services/npms-io/npms-io-score.tester.js b/services/npms-io/npms-io-score.tester.js
new file mode 100644
index 0000000000000..b7ee9b3a577d8
--- /dev/null
+++ b/services/npms-io/npms-io-score.tester.js
@@ -0,0 +1,61 @@
+import { createServiceTester } from '../tester.js'
+import { isPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('should show final score').get('/final-score/vue.json').expectBadge({
+ label: 'score',
+ message: isPercentage,
+})
+
+t.create('should show color')
+ .get('/final-score/mock-for-package-score.json')
+ .intercept(nock => {
+ nock.enableNetConnect()
+
+ return nock('https://api.npms.io', { allowUnmocked: true })
+ .get('/v2/package/mock-for-package-score')
+ .reply(200, {
+ score: {
+ final: 0.89,
+ },
+ })
+ })
+ .expectBadge({
+ label: 'score',
+ message: isPercentage,
+ color: 'yellowgreen',
+ })
+
+t.create('should show final score with scope')
+ .get('/final-score/@vue/cli.json')
+ .expectBadge({
+ label: 'score',
+ message: isPercentage,
+ })
+
+t.create('should show maintenance')
+ .get('/maintenance-score/vue.json')
+ .expectBadge({
+ label: 'maintenance',
+ message: isPercentage,
+ })
+
+t.create('should show popularity')
+ .get('/popularity-score/vue.json')
+ .expectBadge({
+ label: 'popularity',
+ message: isPercentage,
+ })
+
+t.create('should show quality').get('/quality-score/vue.json').expectBadge({
+ label: 'quality',
+ message: isPercentage,
+})
+
+t.create('unknown package')
+ .get('/final-score/npm-api-does-not-have-this-package.json')
+ .expectBadge({
+ label: 'score',
+ message: 'package not found or too new',
+ color: 'red',
+ })
diff --git a/services/nsp/nsp.service.js b/services/nsp/nsp.service.js
deleted file mode 100644
index ef85af672807f..0000000000000
--- a/services/nsp/nsp.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- route: {
- base: 'nsp/npm',
- pattern: ':various*',
- },
- label: 'nsp',
- category: 'other',
- dateAdded: new Date('2018-12-13'),
-})
diff --git a/services/nsp/nsp.tester.js b/services/nsp/nsp.tester.js
deleted file mode 100644
index 11a33eae1b87c..0000000000000
--- a/services/nsp/nsp.tester.js
+++ /dev/null
@@ -1,37 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'nsp',
- title: 'nsp',
- pathPrefix: '/nsp/npm',
-}))
-
-t.create('no longer available (previously package)')
- .get('/gh-badges.json')
- .expectBadge({
- label: 'nsp',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously package version)')
- .get('/gh-badges/2.1.0.json')
- .expectBadge({
- label: 'nsp',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously scoped package)')
- .get('/@babel/core.json')
- .expectBadge({
- label: 'nsp',
- message: 'no longer available',
- })
-
-t.create('no longer available (previously scoped package version)')
- .get('/@babel/core/7.1.6.json')
- .expectBadge({
- label: 'nsp',
- message: 'no longer available',
- })
diff --git a/services/nuget-fixtures.js b/services/nuget-fixtures.js
deleted file mode 100644
index c4e0fe30394f9..0000000000000
--- a/services/nuget-fixtures.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const queryIndex = JSON.stringify({
- resources: [
- {
- '@id': 'https://api-v2v3search-0.nuget.org/query',
- '@type': 'SearchQueryService',
- },
- ],
-})
-
-const nuGetV3VersionJsonWithDash = JSON.stringify({
- data: [
- {
- totalDownloads: 0,
- versions: [{ version: '1.2-beta' }],
- },
- ],
-})
-const nuGetV3VersionJsonFirstCharZero = JSON.stringify({
- data: [
- {
- totalDownloads: 0,
- versions: [{ version: '0.35' }],
- },
- ],
-})
-const nuGetV3VersionJsonFirstCharNotZero = JSON.stringify({
- data: [
- {
- totalDownloads: 0,
- versions: [{ version: '1.2.7' }],
- },
- ],
-})
-
-const nuGetV3VersionJsonBuildMetadataWithDash = JSON.stringify({
- data: [
- {
- totalDownloads: 0,
- versions: [
- {
- version: '1.16.0+388',
- },
- {
- version: '1.17.0+1b81349-429',
- },
- ],
- },
- ],
-})
-
-module.exports = {
- queryIndex,
- nuGetV3VersionJsonWithDash,
- nuGetV3VersionJsonFirstCharZero,
- nuGetV3VersionJsonFirstCharNotZero,
- nuGetV3VersionJsonBuildMetadataWithDash,
-}
diff --git a/services/nuget/nuget-helpers.js b/services/nuget/nuget-helpers.js
index 849cac3ef6957..d24f59d310241 100644
--- a/services/nuget/nuget-helpers.js
+++ b/services/nuget/nuget-helpers.js
@@ -1,24 +1,7 @@
-'use strict'
-
-const { metric, addv } = require('../text-formatters')
-const { downloadCount: downloadCountColor } = require('../color-formatters')
-
-function renderVersionBadge({ version, feed }) {
- let color
- if (version.includes('-')) {
- color = 'yellow'
- } else if (version.startsWith('0')) {
- color = 'orange'
- } else {
- color = 'blue'
- }
-
- return {
- message: addv(version),
- color,
- label: feed,
- }
-}
+import semver from 'semver'
+import { metric } from '../text-formatters.js'
+import { downloadCount as downloadCountColor } from '../color-formatters.js'
+import { getCachedResource } from '../../core/base-service/resource-cache.js'
function renderDownloadBadge({ downloads }) {
return {
@@ -40,8 +23,69 @@ function odataToObject(odata) {
return result
}
-module.exports = {
- renderVersionBadge,
+function randomElementFrom(items) {
+ const index = Math.floor(Math.random() * items.length)
+ return items[index]
+}
+
+/*
+ * Hit the service index endpoint and return a {serviceType} URL, chosen
+ * at random. Cache the responses, but return a different random URL each time.
+ */
+async function searchServiceUrl(baseUrl, serviceType = 'SearchQueryService') {
+ // Should we really be caching all these NuGet feeds in memory?
+ const searchQueryServices = await getCachedResource({
+ url: `${baseUrl}/index.json`,
+ // The endpoint changes once per year (ie, a period of n = 1 year).
+ // We minimize the users' waiting time for information.
+ // With l = latency to fetch the endpoint and x = endpoint update period
+ // both in years, the yearly number of queries for the endpoint are 1/x,
+ // and when the endpoint changes, we wait for up to x years to get the
+ // right endpoint.
+ // So the waiting time within n years is n*l/x + x years, for which a
+ // derivation yields an optimum at x = sqrt(n*l), roughly 42 minutes.
+ ttl: 42 * 60 * 1000,
+ scraper: json =>
+ json.resources.filter(resource => resource['@type'] === serviceType),
+ })
+ return randomElementFrom(searchQueryServices)['@id']
+}
+
+/*
+ * Strip Build MetaData
+ * Nuget versions may include an optional "build metadata" clause,
+ * separated from the version by a + character.
+ */
+function stripBuildMetadata(version) {
+ return version.split('+')[0]
+}
+
+/*
+ * Select latest version from NuGet feed, filtering-out prerelease versions if needed
+ */
+function selectVersion(versions, includePrereleases) {
+ if (includePrereleases) {
+ return versions.slice(-1).pop()
+ } else {
+ const filtered = versions.filter(i => {
+ if (semver.valid(i)) {
+ return !semver.prerelease(i)
+ } else {
+ return !i.includes('-')
+ }
+ })
+ if (filtered.length > 0) {
+ return filtered.slice(-1).pop()
+ } else {
+ return versions.slice(-1).pop()
+ }
+ }
+}
+
+export {
renderDownloadBadge,
odataToObject,
+ searchServiceUrl,
+ stripBuildMetadata,
+ selectVersion,
}
diff --git a/services/nuget/nuget-helpers.spec.js b/services/nuget/nuget-helpers.spec.js
index bdfb08148c760..9cc8ea5b4af09 100644
--- a/services/nuget/nuget-helpers.spec.js
+++ b/services/nuget/nuget-helpers.spec.js
@@ -1,31 +1,29 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const { renderVersionBadge, odataToObject } = require('./nuget-helpers')
-
-describe('NuGet helpers', function() {
- test(renderVersionBadge, () => {
- given({ version: '1.2-beta' }).expect({
- label: undefined,
- message: 'v1.2-beta',
- color: 'yellow',
- })
- given({ version: '0.35' }).expect({
- label: undefined,
- message: 'v0.35',
- color: 'orange',
- })
- given({ version: '1.2.7' }).expect({
- label: undefined,
- message: 'v1.2.7',
- color: 'blue',
- })
- })
+import { test, given } from 'sazerac'
+import {
+ odataToObject,
+ stripBuildMetadata,
+ selectVersion,
+} from './nuget-helpers.js'
+describe('NuGet helpers', function () {
test(odataToObject, () => {
given({ 'm:properties': { 'd:Version': '1.2.3' } }).expect({
Version: '1.2.3',
})
given(undefined).expect(undefined)
})
+
+ test(stripBuildMetadata, () => {
+ given('1.0.0').expect('1.0.0')
+ given('1.0.0+1').expect('1.0.0')
+ })
+
+ test(selectVersion, () => {
+ given(['1.0.0', '1.0.1'], false).expect('1.0.1')
+ given(['1.0.0', '1.0.1'], true).expect('1.0.1')
+ given(['1.0.0', '1.0.1-pre'], false).expect('1.0.0')
+ given(['1.0.0', '1.0.1-pre'], true).expect('1.0.1-pre')
+ given(['1.0.0', '1.0.1.0.1.0-pre'], false).expect('1.0.0')
+ given(['1.0.0', '1.0.1.0.1.0-pre'], true).expect('1.0.1.0.1.0-pre')
+ })
})
diff --git a/services/nuget/nuget-v2-service-family.js b/services/nuget/nuget-v2-service-family.js
index d93db0fb3cd0e..0b384226daf40 100644
--- a/services/nuget/nuget-v2-service-family.js
+++ b/services/nuget/nuget-v2-service-family.js
@@ -1,41 +1,31 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, BaseXmlService, NotFound, redirector } = require('..')
-const {
- renderVersionBadge,
- renderDownloadBadge,
- odataToObject,
-} = require('./nuget-helpers')
+import Joi from 'joi'
+import qs from 'qs'
+import { nonNegativeInteger } from '../validators.js'
+import {
+ BaseXmlService,
+ NotFound,
+ retiredService,
+ pathParams,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { renderDownloadBadge, odataToObject } from './nuget-helpers.js'
function createFilter({ packageName, includePrereleases }) {
const releaseTypeFilter = includePrereleases
? 'IsAbsoluteLatestVersion eq true'
: 'IsLatestVersion eq true'
- return `Id eq '${packageName}' and ${releaseTypeFilter}`
+ return `tolower(Id) eq '${packageName.toLowerCase()}' and ${releaseTypeFilter}`
}
-const jsonSchema = Joi.object({
- d: Joi.object({
- results: Joi.array()
- .items(
- Joi.object({
- Version: Joi.string(),
- NormalizedVersion: Joi.string(),
- DownloadCount: nonNegativeInteger,
- })
- )
- .max(1)
- .default([]),
- }).required(),
-}).required()
+const versionSchema = Joi.alternatives(Joi.string(), Joi.number())
const xmlSchema = Joi.object({
feed: Joi.object({
entry: Joi.object({
'm:properties': Joi.object({
- 'd:Version': Joi.alternatives(Joi.string(), Joi.number()),
+ 'd:Version': versionSchema,
'd:NormalizedVersion': Joi.string(),
'd:DownloadCount': nonNegativeInteger,
'd:Tags': Joi.string(),
@@ -50,38 +40,29 @@ const queryParamSchema = Joi.object({
async function fetch(
serviceInstance,
- { odataFormat, baseUrl, packageName, includePrereleases = false }
+ { baseUrl, packageName, includePrereleases = false },
) {
const url = `${baseUrl}/Packages()`
- const qs = { $filter: createFilter({ packageName, includePrereleases }) }
-
- let packageData
- if (odataFormat === 'xml') {
- const data = await serviceInstance._requestXml({
- schema: xmlSchema,
- url,
- options: { qs },
- })
- packageData = odataToObject(data.feed.entry)
- } else if (odataFormat === 'json') {
- const data = await serviceInstance._requestJson({
- schema: jsonSchema,
- url,
- options: {
- headers: { Accept: 'application/atom+json,application/json' },
- qs,
- },
- })
- packageData = data.d.results[0]
- } else {
- throw Error(`Unsupported Atom OData format: ${odataFormat}`)
- }
+ const searchParams = qs.stringify(
+ {
+ $filter: createFilter({ packageName, includePrereleases }),
+ },
+ { encode: false },
+ )
+
+ const data = await serviceInstance._requestXml({
+ schema: xmlSchema,
+ url: `${url}?${searchParams}`,
+ options: {
+ headers: { Accept: 'application/atom+xml,application/xml' },
+ },
+ })
+ const packageData = odataToObject(data.feed.entry)
if (packageData) {
return packageData
} else if (!includePrereleases) {
return fetch(serviceInstance, {
- odataFormat,
baseUrl,
packageName,
includePrereleases: true,
@@ -105,117 +86,91 @@ function createServiceFamily({
defaultLabel,
serviceBaseUrl,
apiBaseUrl,
- odataFormat,
examplePackageName,
- exampleVersion,
- examplePrereleaseVersion,
- exampleDownloadCount,
}) {
- let Base
- if (odataFormat === 'xml') {
- Base = BaseXmlService
- } else if (odataFormat === 'json') {
- Base = BaseJsonService
- } else {
- throw Error(`Unsupported Atom OData format: ${odataFormat}`)
- }
+ class NugetVersionService extends BaseXmlService {
+ static name = `${name}Version`
- class NugetVersionService extends Base {
- static get name() {
- return `${name}Version`
- }
-
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: `${serviceBaseUrl}/v`,
- pattern: ':packageName',
- queryParamSchema,
- }
+ static route = {
+ base: `${serviceBaseUrl}/v`,
+ pattern: ':packageName',
+ queryParamSchema,
}
- static get examples() {
- if (!title) return []
-
- return [
- {
- title: `${title} Version`,
- namedParams: { packageName: examplePackageName },
- staticPreview: this.render({ version: exampleVersion }),
- },
- {
- title: `${title} Version (including pre-releases)`,
- namedParams: { packageName: examplePackageName },
- queryParams: { include_prereleases: null },
- staticPreview: this.render({ version: examplePrereleaseVersion }),
+ static get openApi() {
+ if (!title) return {}
+
+ const key = `/${serviceBaseUrl}/v/{packageName}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `${title} Version`,
+ parameters: [
+ pathParam({ name: 'packageName', example: examplePackageName }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
},
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: defaultLabel,
}
+ return route
}
- static render(props) {
- return renderVersionBadge(props)
+ static defaultBadgeData = {
+ label: defaultLabel,
}
async handle({ packageName }, queryParams) {
const packageData = await fetch(this, {
- odataFormat,
baseUrl: apiBaseUrl,
packageName,
includePrereleases: queryParams.include_prereleases !== undefined,
})
- const version = packageData.NormalizedVersion || packageData.Version
- return this.constructor.render({ version })
+ const version = packageData.NormalizedVersion || `${packageData.Version}`
+ return renderVersionBadge({ version })
}
}
- const NugetVersionRedirector = redirector({
+ const NugetVersionRedirector = retiredService({
category: 'version',
+ label: defaultLabel,
route: {
base: `${serviceBaseUrl}/vpre`,
pattern: ':packageName',
},
- transformPath: ({ packageName }) => `/${serviceBaseUrl}/v/${packageName}`,
- transformQueryParams: params => ({
- include_prereleases: null,
- }),
- dateAdded: new Date('2019-12-15'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
})
- class NugetDownloadService extends Base {
- static get name() {
- return `${name}Downloads`
- }
+ class NugetDownloadService extends BaseXmlService {
+ static name = `${name}Downloads`
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get route() {
- return {
- base: serviceBaseUrl,
- pattern: 'dt/:packageName',
- }
+ static route = {
+ base: serviceBaseUrl,
+ pattern: 'dt/:packageName',
}
- static get examples() {
- if (!title) return []
-
- return [
- {
- title,
- namedParams: { packageName: examplePackageName },
- staticPreview: this.render({ downloads: exampleDownloadCount }),
+ static get openApi() {
+ if (!title) return {}
+
+ const key = `/${serviceBaseUrl}/dt/{packageName}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `${title} Downloads`,
+ parameters: pathParams({
+ name: 'packageName',
+ example: examplePackageName,
+ }),
},
- ]
+ }
+ return route
}
static render(props) {
@@ -224,7 +179,6 @@ function createServiceFamily({
async handle({ packageName }) {
const packageData = await fetch(this, {
- odataFormat,
baseUrl: apiBaseUrl,
packageName,
})
@@ -236,8 +190,4 @@ function createServiceFamily({
return { NugetVersionService, NugetVersionRedirector, NugetDownloadService }
}
-module.exports = {
- createFilter,
- fetch,
- createServiceFamily,
-}
+export { createFilter, fetch, createServiceFamily }
diff --git a/services/nuget/nuget-v3-service-family.js b/services/nuget/nuget-v3-service-family.js
index 20ceb04d96cec..b6b5d93d8baeb 100644
--- a/services/nuget/nuget-v3-service-family.js
+++ b/services/nuget/nuget-v3-service-family.js
@@ -1,12 +1,13 @@
-'use strict'
-
-const { promisify } = require('util')
-const Joi = require('@hapi/joi')
-const semver = require('semver')
-const { regularUpdate } = require('../../core/legacy/regular-update')
-const RouteBuilder = require('../route-builder')
-const { BaseJsonService, NotFound } = require('..')
-const { renderVersionBadge, renderDownloadBadge } = require('./nuget-helpers')
+import Joi from 'joi'
+import RouteBuilder from '../route-builder.js'
+import { BaseJsonService, NotFound } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import {
+ renderDownloadBadge,
+ searchServiceUrl,
+ stripBuildMetadata,
+ selectVersion,
+} from './nuget-helpers.js'
/*
* Build the Shields service URL object for the given service configuration. Return
@@ -47,37 +48,6 @@ function apiUrl({ withTenant, apiBaseUrl, apiDomain, tenant, withFeed, feed }) {
return result
}
-function randomElementFrom(items) {
- const index = Math.floor(Math.random() * items.length)
- return items[index]
-}
-
-/*
- * Hit the service index endpoint and return a SearchQueryService URL, chosen
- * at random. Cache the responses, but return a different random URL each time.
- */
-async function searchQueryServiceUrl(baseUrl) {
- // Should we really be caching all these NuGet feeds in memory?
- const searchQueryServices = await promisify(regularUpdate)({
- url: `${baseUrl}/index.json`,
- // The endpoint changes once per year (ie, a period of n = 1 year).
- // We minimize the users' waiting time for information.
- // With l = latency to fetch the endpoint and x = endpoint update period
- // both in years, the yearly number of queries for the endpoint are 1/x,
- // and when the endpoint changes, we wait for up to x years to get the
- // right endpoint.
- // So the waiting time within n years is n*l/x + x years, for which a
- // derivation yields an optimum at x = sqrt(n*l), roughly 42 minutes.
- intervalMillis: 42 * 60 * 1000,
- json: true,
- scraper: json =>
- json.resources.filter(
- resource => resource['@type'] === 'SearchQueryService'
- ),
- })
- return randomElementFrom(searchQueryServices)['@id']
-}
-
const schema = Joi.object({
data: Joi.array()
.items(
@@ -86,39 +56,30 @@ const schema = Joi.object({
.items(
Joi.object({
version: Joi.string().required(),
- })
+ }),
)
.default([]),
totalDownloads: Joi.number().integer(),
totaldownloads: Joi.number().integer(),
- })
+ }),
)
.max(1)
.default([]),
}).required()
-/*
- * Strip Build MetaData
- * Nuget versions may include an optional "build metadata" clause,
- * seperated from the version by a + character.
- */
-function stripBuildMetadata(version) {
- return version.split('+')[0]
-}
-
/*
* Get information about a single package.
*/
async function fetch(
serviceInstance,
- { baseUrl, packageName, includePrereleases = false }
+ { baseUrl, packageName, includePrereleases = false },
) {
- const json = await serviceInstance._requestJson({
+ return serviceInstance._requestJson({
schema,
- url: await searchQueryServiceUrl(baseUrl),
+ url: await searchServiceUrl(baseUrl, 'SearchQueryService'),
options: {
- qs: {
- q: `packageid:${encodeURIComponent(packageName.toLowerCase())}`,
+ searchParams: {
+ q: `packageid:${packageName.toLowerCase()}`,
// Include prerelease versions.
prerelease: 'true',
// Include packages with SemVer 2 version numbers.
@@ -126,12 +87,6 @@ async function fetch(
},
},
})
-
- if (json.data.length === 1) {
- return json.data[0]
- } else {
- throw new NotFound({ prettyMessage: 'package not found' })
- }
}
/*
@@ -159,32 +114,36 @@ function createServiceFamily({
withFeed = true,
}) {
class NugetVersionService extends BaseJsonService {
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return buildRoute({ serviceBaseUrl, withTenant, withFeed })
- .push('(v|vpre)', 'which')
- .push('(.+?)', 'packageName')
- .toObject()
- }
+ static route = buildRoute({ serviceBaseUrl, withTenant, withFeed })
+ .push('(v|vpre)', 'which')
+ .push('(.+?)', 'packageName')
+ .toObject()
- static get examples() {
- return []
- }
+ static openApi = {}
- static get defaultBadgeData() {
- return {
- label: defaultLabel,
- }
+ static defaultBadgeData = {
+ label: defaultLabel,
}
- static render(props) {
- return renderVersionBadge(props)
+ /*
+ * Extract version information from the raw package info.
+ */
+ transform({ json, includePrereleases }) {
+ if (json.data.length === 1 && json.data[0].versions.length > 0) {
+ const { versions: packageVersions } = json.data[0]
+ const versions = packageVersions.map(item =>
+ stripBuildMetadata(item.version),
+ )
+ return selectVersion(versions, includePrereleases)
+ } else {
+ throw new NotFound({ prettyMessage: 'package not found' })
+ }
}
async handle({ tenant, feed, which, packageName }) {
+ const includePrereleases = which === 'vpre'
const baseUrl = apiUrl({
withTenant,
apiBaseUrl,
@@ -193,48 +152,40 @@ function createServiceFamily({
withFeed,
feed,
})
- let { versions } = await fetch(this, { baseUrl, packageName })
- versions = versions.map(item => ({
- version: stripBuildMetadata(item.version),
- }))
- let latest = versions.slice(-1).pop()
- const includePrereleases = which === 'vpre'
- if (!includePrereleases) {
- const filtered = versions.filter(item => {
- if (semver.valid(item.version)) {
- return !semver.prerelease(item.version)
- }
- return !item.version.includes('-')
- })
- if (filtered.length) {
- latest = filtered.slice(-1).pop()
- }
- }
- const { version } = latest
- return this.constructor.render({ version, feed })
+ const json = await fetch(this, { baseUrl, packageName })
+ const version = this.transform({ json, includePrereleases })
+ return renderVersionBadge({ version, defaultLabel: feed })
}
}
class NugetDownloadService extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get route() {
- return buildRoute({ serviceBaseUrl, withTenant, withFeed })
- .push('dt')
- .push('(.+?)', 'packageName')
- .toObject()
- }
+ static route = buildRoute({ serviceBaseUrl, withTenant, withFeed })
+ .push('dt')
+ .push('(.+?)', 'packageName')
+ .toObject()
- static get examples() {
- return []
- }
+ static openApi = {}
static render(props) {
return renderDownloadBadge(props)
}
+ /*
+ * Extract download count from the raw package.
+ */
+ transform({ json }) {
+ if (json.data.length === 1) {
+ const packageInfo = json.data[0]
+ // Official NuGet server uses "totalDownloads" whereas MyGet uses
+ // "totaldownloads" (lowercase D). Ugh.
+ return packageInfo.totalDownloads || packageInfo.totaldownloads || 0
+ } else {
+ throw new NotFound({ prettyMessage: 'package not found' })
+ }
+ }
+
async handle({ tenant, feed, which, packageName }) {
const baseUrl = apiUrl({
withTenant,
@@ -244,13 +195,8 @@ function createServiceFamily({
withFeed,
feed,
})
- const packageInfo = await fetch(this, { baseUrl, packageName })
-
- // Official NuGet server uses "totalDownloads" whereas MyGet uses
- // "totaldownloads" (lowercase D). Ugh.
- const downloads =
- packageInfo.totalDownloads || packageInfo.totaldownloads || 0
-
+ const json = await fetch(this, { baseUrl, packageName })
+ const downloads = this.transform({ json })
return this.constructor.render({ downloads })
}
}
@@ -261,6 +207,4 @@ function createServiceFamily({
}
}
-module.exports = {
- createServiceFamily,
-}
+export { createServiceFamily }
diff --git a/services/nuget/nuget-v3-service-family.spec.js b/services/nuget/nuget-v3-service-family.spec.js
new file mode 100644
index 0000000000000..f2f0bcd0ffde9
--- /dev/null
+++ b/services/nuget/nuget-v3-service-family.spec.js
@@ -0,0 +1,89 @@
+import { test, given } from 'sazerac'
+import { createServiceFamily } from './nuget-v3-service-family.js'
+
+const { NugetVersionService, NugetDownloadService } = createServiceFamily({
+ defaultLabel: 'nuget',
+ serviceBaseUrl: 'nuget',
+ apiBaseUrl: 'test',
+})
+
+function versionJson(versions) {
+ return {
+ data: [
+ {
+ versions: versions.map(v => ({
+ version: v,
+ })),
+ },
+ ],
+ }
+}
+
+function downloadsJson(payload) {
+ return {
+ data: [payload],
+ }
+}
+
+const noDataJson = { data: [] }
+const tooMuchDataJson = { data: [{}, {}] }
+
+describe('Nuget Version service', function () {
+ test(NugetVersionService.prototype.transform, () => {
+ given({ json: versionJson(['1.0.0']), includePrereleases: false }).expect(
+ '1.0.0',
+ )
+ given({
+ json: versionJson(['1.0.0', '1.0.1']),
+ includePrereleases: false,
+ }).expect('1.0.1')
+ given({
+ json: versionJson(['1.0.0', '1.0.1-beta1']),
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ json: versionJson(['1.0.0', '1.0.1-beta1']),
+ includePrereleases: true,
+ }).expect('1.0.1-beta1')
+
+ given({
+ json: versionJson(['1.0.0+1', '1.0.1-beta1+1']),
+ includePrereleases: false,
+ }).expect('1.0.0')
+ given({
+ json: versionJson(['1.0.0+1', '1.0.1-beta1+1']),
+ includePrereleases: true,
+ }).expect('1.0.1-beta1')
+
+ given({ json: versionJson([]), includePrereleases: false }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ json: versionJson([]), includePrereleases: true }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ json: noDataJson, includePrereleases: false }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ json: noDataJson, includePrereleases: true }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ json: tooMuchDataJson, includePrereleases: false }).expectError(
+ 'Not Found: package not found',
+ )
+ given({ json: tooMuchDataJson, includePrereleases: true }).expectError(
+ 'Not Found: package not found',
+ )
+ })
+})
+
+describe('Nuget Download service', function () {
+ test(NugetDownloadService.prototype.transform, () => {
+ given({ json: downloadsJson({ totalDownloads: 10 }) }).expect(10)
+ given({ json: downloadsJson({ totaldownloads: 11 }) }).expect(11)
+ given({ json: downloadsJson({ other: 11 }) }).expect(0)
+ given({ json: downloadsJson({}) }).expect(0)
+
+ given({ json: noDataJson }).expectError('Not Found: package not found')
+ given({ json: tooMuchDataJson }).expectError('Not Found: package not found')
+ })
+})
diff --git a/services/nuget/nuget.service.js b/services/nuget/nuget.service.js
index 0300773dedc11..58316dec21373 100644
--- a/services/nuget/nuget.service.js
+++ b/services/nuget/nuget.service.js
@@ -1,48 +1,47 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { createServiceFamily } from './nuget-v3-service-family.js'
-const { createServiceFamily } = require('./nuget-v3-service-family')
-
-const {
- NugetVersionService: Version,
- NugetDownloadService: Downloads,
-} = createServiceFamily({
- defaultLabel: 'nuget',
- serviceBaseUrl: 'nuget',
- apiBaseUrl: 'https://api.nuget.org/v3',
- withTenant: false,
- withFeed: false,
-})
+const { NugetVersionService: Version, NugetDownloadService: Downloads } =
+ createServiceFamily({
+ defaultLabel: 'nuget',
+ serviceBaseUrl: 'nuget',
+ apiBaseUrl: 'https://api.nuget.org/v3',
+ withTenant: false,
+ withFeed: false,
+ })
class NugetVersionService extends Version {
- static get examples() {
- return [
- {
- title: 'Nuget',
- pattern: 'v/:packageName',
- namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
- staticPreview: this.render({ version: '5.2.4' }),
- },
- {
- title: 'Nuget (with prereleases)',
- pattern: 'vpre/:packageName',
- namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
- staticPreview: this.render({ version: '5.2.5-preview1' }),
+ static openApi = {
+ '/nuget/{variant}/{packageName}': {
+ get: {
+ summary: 'NuGet Version',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'v',
+ schema: { type: 'variant', enum: ['v', 'vpre'] },
+ description:
+ 'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
+ },
+ { name: 'packageName', example: 'Microsoft.AspNet.Mvc' },
+ ),
},
- ]
+ },
}
}
class NugetDownloadService extends Downloads {
- static get examples() {
- return [
- {
- title: 'Nuget',
- pattern: 'dt/:packageName',
- namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
- staticPreview: this.render({ downloads: 49e6 }),
+ static openApi = {
+ '/nuget/dt/{packageName}': {
+ get: {
+ summary: 'NuGet Downloads',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'Microsoft.AspNet.Mvc',
+ }),
},
- ]
+ },
}
}
-module.exports = { NugetVersionService, NugetDownloadService }
+export { NugetVersionService, NugetDownloadService }
diff --git a/services/nuget/nuget.tester.js b/services/nuget/nuget.tester.js
index 4d8e6e1685541..2f5290f49df63 100644
--- a/services/nuget/nuget.tester.js
+++ b/services/nuget/nuget.tester.js
@@ -1,21 +1,37 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const {
+import { ServiceTester } from '../tester.js'
+import {
isMetric,
isVPlusDottedVersionNClauses,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const {
- queryIndex,
- nuGetV3VersionJsonWithDash,
- nuGetV3VersionJsonFirstCharZero,
- nuGetV3VersionJsonFirstCharNotZero,
- nuGetV3VersionJsonBuildMetadataWithDash,
-} = require('../nuget-fixtures')
-const { invalidJSON } = require('../response-fixtures')
-
-const t = (module.exports = new ServiceTester({ id: 'nuget', title: 'NuGet' }))
+} from '../test-validators.js'
+import { invalidJSON } from '../response-fixtures.js'
+
+export const t = new ServiceTester({ id: 'nuget', title: 'NuGet' })
+
+const queryIndex = JSON.stringify({
+ resources: [
+ {
+ '@id': 'https://api-v2v3search-0.nuget.org/query',
+ '@type': 'SearchQueryService',
+ },
+ ],
+})
+
+const nuGetV3VersionJsonBuildMetadataWithDash = JSON.stringify({
+ data: [
+ {
+ totalDownloads: 0,
+ versions: [
+ {
+ version: '1.16.0+388',
+ },
+ {
+ version: '1.17.0+1b81349-429',
+ },
+ ],
+ },
+ ],
+})
// downloads
@@ -33,16 +49,14 @@ t.create('total downloads (not found)')
t.create('total downloads (unexpected second response)')
.get('/dt/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
+ nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
+ '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
- .reply(invalidJSON)
+ .reply(invalidJSON),
)
.expectBadge({ label: 'downloads', message: 'unparseable json response' })
@@ -55,78 +69,34 @@ t.create('version (valid)')
message: isVPlusDottedVersionNClauses,
})
-t.create('version (yellow badge)')
- .get('/v/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonWithDash)
- )
- .expectBadge({
- label: 'nuget',
- message: 'v1.2-beta',
- color: 'yellow',
- })
-
-t.create('version (orange badge)')
- .get('/v/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharZero)
- )
- .expectBadge({
- label: 'nuget',
- message: 'v0.35',
- color: 'orange',
- })
+t.create('version (not found)')
+ .get('/v/not-a-real-package.json')
+ .expectBadge({ label: 'nuget', message: 'package not found' })
-t.create('version (blue badge)')
+t.create('version (unexpected second response)')
.get('/v/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
+ nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
+ '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
- .reply(200, nuGetV3VersionJsonFirstCharNotZero)
+ .reply(invalidJSON),
)
- .expectBadge({
- label: 'nuget',
- message: 'v1.2.7',
- color: 'blue',
- })
+ .expectBadge({ label: 'nuget', message: 'unparseable json response' })
// https://github.com/badges/shields/issues/4219
t.create('version (build metadata with -)')
.get('/v/MongoFramework.json')
.intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
+ nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get('/query?q=packageid%3Amongoframework&prerelease=true&semVerLevel=2')
- .reply(200, nuGetV3VersionJsonBuildMetadataWithDash)
+ .reply(200, nuGetV3VersionJsonBuildMetadataWithDash),
)
.expectBadge({
label: 'nuget',
@@ -134,26 +104,6 @@ t.create('version (build metadata with -)')
color: 'blue',
})
-t.create('version (not found)')
- .get('/v/not-a-real-package.json')
- .expectBadge({ label: 'nuget', message: 'package not found' })
-
-t.create('version (unexpected second response)')
- .get('/v/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(invalidJSON)
- )
- .expectBadge({ label: 'nuget', message: 'unparseable json response' })
-
// version (pre)
t.create('version (pre) (valid)')
@@ -163,82 +113,29 @@ t.create('version (pre) (valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
-t.create('version (pre) (yellow badge)')
- .get('/vpre/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonWithDash)
- )
- .expectBadge({
- label: 'nuget',
- message: 'v1.2-beta',
- color: 'yellow',
- })
+t.create('version (pre) (not found)')
+ .get('/vpre/not-a-real-package.json')
+ .expectBadge({ label: 'nuget', message: 'package not found' })
-t.create('version (pre) (orange badge)')
- .get('/vpre/Microsoft.AspNetCore.Mvc.json')
+// https://github.com/badges/shields/issues/11513
+t.create('version (special characters - encoded)')
+ .get('/v/NC%C3%B2dexSDK.json')
.intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
+ nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharZero)
+ .get('/query?q=packageid%3Anc%C3%B2dexsdk&prerelease=true&semVerLevel=2')
+ .reply(200, {
+ data: [
+ {
+ versions: [{ version: '0.0.1' }],
+ },
+ ],
+ }),
)
.expectBadge({
label: 'nuget',
- message: 'v0.35',
+ message: 'v0.0.1',
color: 'orange',
})
-
-t.create('version (pre) (blue badge)')
- .get('/vpre/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(200, nuGetV3VersionJsonFirstCharNotZero)
- )
- .expectBadge({
- label: 'nuget',
- message: 'v1.2.7',
- color: 'blue',
- })
-
-t.create('version (pre) (not found)')
- .get('/vpre/not-a-real-package.json')
- .expectBadge({ label: 'nuget', message: 'package not found' })
-
-t.create('version (pre) (unexpected second response)')
- .get('/vpre/Microsoft.AspNetCore.Mvc.json')
- .intercept(nock =>
- nock('https://api.nuget.org')
- .get('/v3/index.json')
- .reply(200, queryIndex)
- )
- .intercept(nock =>
- nock('https://api-v2v3search-0.nuget.org')
- .get(
- '/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2'
- )
- .reply(invalidJSON)
- )
- .expectBadge({ label: 'nuget', message: 'unparseable json response' })
diff --git a/services/nycrc/nycrc.service.js b/services/nycrc/nycrc.service.js
index fb38a764698fb..fc461c2769560 100644
--- a/services/nycrc/nycrc.service.js
+++ b/services/nycrc/nycrc.service.js
@@ -1,96 +1,79 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage } = require('../color-formatters')
-const {
- ConditionalGithubAuthV3Service,
-} = require('../github/github-auth-service')
-const { fetchJsonFromRepo } = require('../github/github-common-fetch')
-const { InvalidParameter, InvalidResponse, NotFound } = require('..')
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
+import { fetchJsonFromRepo } from '../github/github-common-fetch.js'
+import {
+ InvalidParameter,
+ InvalidResponse,
+ NotFound,
+ pathParam,
+ queryParam,
+} from '../index.js'
const nycrcSchema = Joi.object({
- branches: Joi.number()
- .min(0)
- .max(100),
- lines: Joi.number()
- .min(0)
- .max(100),
- functions: Joi.number()
- .min(0)
- .max(100),
+ branches: Joi.number().min(0).max(100),
+ lines: Joi.number().min(0).max(100),
+ functions: Joi.number().min(0).max(100),
}).required()
const pkgJSONSchema = Joi.object({
c8: Joi.object({
- branches: Joi.number()
- .min(0)
- .max(100),
- lines: Joi.number()
- .min(0)
- .max(100),
- functions: Joi.number()
- .min(0)
- .max(100),
+ branches: Joi.number().min(0).max(100),
+ lines: Joi.number().min(0).max(100),
+ functions: Joi.number().min(0).max(100),
}).optional(),
nyc: Joi.object({
- branches: Joi.number()
- .min(0)
- .max(100),
- lines: Joi.number()
- .min(0)
- .max(100),
- functions: Joi.number()
- .min(0)
- .max(100),
+ branches: Joi.number().min(0).max(100),
+ lines: Joi.number().min(0).max(100),
+ functions: Joi.number().min(0).max(100),
}).optional(),
}).required()
-const documentation = `
- Create a code coverage badge, based on thresholds stored in a
- .nycrc config file
- on GitHub.
-
`
+const description = `
+Create a code coverage badge, based on thresholds stored in a
+[.nycrc config file](https://github.com/istanbuljs/nyc#common-configuration-options)
+on GitHub.
+`
const validThresholds = ['branches', 'lines', 'functions']
-module.exports = class Nycrc extends ConditionalGithubAuthV3Service {
- static get category() {
- return 'coverage'
- }
+export default class Nycrc extends ConditionalGithubAuthV3Service {
+ static category = 'coverage'
- static get route() {
- return {
- base: 'nycrc',
- // TODO: eventually add support for .yml and .yaml:
- pattern: ':user/:repo',
- queryParamSchema: Joi.object({
- config: Joi.string()
- .regex(/(.*\.nycrc)|(.*\.json$)/)
- .default('.nycrc'),
- // Allow the default threshold detection logic to be overridden, .e.g.,
- // favoring lines over branches:
- preferredThreshold: Joi.string()
- .optional()
- .allow(...validThresholds),
- }).required(),
- }
+ static route = {
+ base: 'nycrc',
+ // TODO: eventually add support for .yml and .yaml:
+ pattern: ':user/:repo',
+ queryParamSchema: Joi.object({
+ config: Joi.string()
+ .regex(/(.*\.nycrc)|(.*\.json$)/)
+ .default('.nycrc'),
+ // Allow the default threshold detection logic to be overridden, .e.g.,
+ // favoring lines over branches:
+ preferredThreshold: Joi.string(),
+ }).required(),
}
- static get examples() {
- return [
- {
- title: 'nycrc config on GitHub',
- namedParams: { user: 'yargs', repo: 'yargs' },
- queryParams: { config: '.nycrc', preferredThreshold: 'lines' },
- staticPreview: this.render({ coverage: 92 }),
- documentation,
+ static openApi = {
+ '/nycrc/{user}/{repo}': {
+ get: {
+ summary: 'nycrc config on GitHub',
+ description,
+ parameters: [
+ pathParam({ name: 'user', example: 'yargs' }),
+ pathParam({ name: 'repo', example: 'yargs' }),
+ queryParam({ name: 'config', example: '.nycrc' }),
+ queryParam({
+ name: 'preferredThreshold',
+ example: 'lines',
+ schema: { type: 'string', enum: validThresholds },
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'min coverage' }
- }
+ static defaultBadgeData = { label: 'min coverage' }
static render({ coverage }) {
return {
@@ -104,7 +87,8 @@ module.exports = class Nycrc extends ConditionalGithubAuthV3Service {
if (preferredThreshold) {
if (!validThresholds.includes(preferredThreshold)) {
throw new InvalidParameter({
- prettyMessage: `threshold must be "branches", "lines", or "functions"`,
+ prettyMessage:
+ 'threshold must be "branches", "lines", or "functions"',
})
}
if (!config[preferredThreshold]) {
@@ -132,7 +116,7 @@ module.exports = class Nycrc extends ConditionalGithubAuthV3Service {
schema: pkgJSONSchema,
user,
repo,
- branch: 'master',
+ branch: 'HEAD',
filename: config,
})
const nycConfig = pkgJson.c8 || pkgJson.nyc
@@ -149,10 +133,10 @@ module.exports = class Nycrc extends ConditionalGithubAuthV3Service {
schema: nycrcSchema,
user,
repo,
- branch: 'master',
+ branch: 'HEAD',
filename: config,
}),
- preferredThreshold
+ preferredThreshold,
)
}
return this.constructor.render({ coverage })
diff --git a/services/nycrc/nycrc.tester.js b/services/nycrc/nycrc.tester.js
index 5f61199e0f8d4..f9e4398d69118 100644
--- a/services/nycrc/nycrc.tester.js
+++ b/services/nycrc/nycrc.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('valid .nycrc')
.get('/yargs/yargs.json?config=.nycrc')
@@ -26,15 +25,15 @@ t.create('.nycrc in monorepo')
.get('/yargs/yargs.json?config=packages/foo/.nycrc.json')
.intercept(nock =>
nock('https://api.github.com')
- .get('/repos/yargs/yargs/contents/packages/foo/.nycrc.json?ref=master')
+ .get('/repos/yargs/yargs/contents/packages/foo/.nycrc.json?ref=HEAD')
.reply(200, {
content: Buffer.from(
JSON.stringify({
lines: 99,
- })
+ }),
).toString('base64'),
encoding: 'base64',
- })
+ }),
)
.expectBadge({ label: 'min coverage', message: isIntegerPercentage })
@@ -42,15 +41,15 @@ t.create('.nycrc with no thresholds')
.get('/yargs/yargs.json?config=.nycrc')
.intercept(nock =>
nock('https://api.github.com')
- .get('/repos/yargs/yargs/contents/.nycrc?ref=master')
+ .get('/repos/yargs/yargs/contents/.nycrc?ref=HEAD')
.reply(200, {
content: Buffer.from(
JSON.stringify({
reporter: 'foo',
- })
+ }),
).toString('base64'),
encoding: 'base64',
- })
+ }),
)
.expectBadge({
label: 'min coverage',
@@ -61,17 +60,17 @@ t.create('package.json with nyc stanza')
.get('/yargs/yargs.json?config=package.json')
.intercept(nock =>
nock('https://api.github.com')
- .get('/repos/yargs/yargs/contents/package.json?ref=master')
+ .get('/repos/yargs/yargs/contents/package.json?ref=HEAD')
.reply(200, {
content: Buffer.from(
JSON.stringify({
nyc: {
lines: 99,
},
- })
+ }),
).toString('base64'),
encoding: 'base64',
- })
+ }),
)
.expectBadge({ label: 'min coverage', message: isIntegerPercentage })
@@ -79,15 +78,15 @@ t.create('package.json with nyc stanza, but no thresholds')
.get('/yargs/yargs.json?config=package.json')
.intercept(nock =>
nock('https://api.github.com')
- .get('/repos/yargs/yargs/contents/package.json?ref=master')
+ .get('/repos/yargs/yargs/contents/package.json?ref=HEAD')
.reply(200, {
content: Buffer.from(
JSON.stringify({
nyc: {},
- })
+ }),
).toString('base64'),
encoding: 'base64',
- })
+ }),
)
.expectBadge({
label: 'min coverage',
diff --git a/services/obs/obs-build-status.js b/services/obs/obs-build-status.js
new file mode 100644
index 0000000000000..c26da4279cbb9
--- /dev/null
+++ b/services/obs/obs-build-status.js
@@ -0,0 +1,34 @@
+import Joi from 'joi'
+import {
+ isBuildStatus as gIsBuildStatus,
+ renderBuildStatusBadge as gRenderBuildStatusBadge,
+} from '../build-status.js'
+
+const localStatuses = {
+ blocked: 'inactive',
+ disabled: 'inactive',
+ finished: 'orange',
+ 'scheduled-warning': 'orange',
+ signing: 'orange',
+ unknown: 'inactive',
+ unresolvable: 'red',
+}
+
+const isBuildStatus = Joi.alternatives().try(
+ gIsBuildStatus,
+ Joi.equal(...Object.keys(localStatuses)),
+)
+
+function renderBuildStatusBadge({ status }) {
+ const color = localStatuses[status]
+ if (color) {
+ return {
+ message: status.toLowerCase(),
+ color,
+ }
+ } else {
+ return gRenderBuildStatusBadge({ status: status.toLowerCase() })
+ }
+}
+
+export { isBuildStatus, renderBuildStatusBadge }
diff --git a/services/obs/obs.service.js b/services/obs/obs.service.js
new file mode 100644
index 0000000000000..d22d63d25016e
--- /dev/null
+++ b/services/obs/obs.service.js
@@ -0,0 +1,79 @@
+import Joi from 'joi'
+import { BaseXmlService, pathParam, queryParam } from '../index.js'
+import { optionalUrl } from '../validators.js'
+import { isBuildStatus, renderBuildStatusBadge } from './obs-build-status.js'
+
+const schema = Joi.object({
+ status: Joi.object({
+ '@_code': isBuildStatus,
+ }).required(),
+}).required()
+
+export default class ObsService extends BaseXmlService {
+ static category = 'build'
+ static route = {
+ base: 'obs',
+ pattern: ':project/:packageName/:repository/:arch',
+ queryParamSchema: Joi.object({
+ instance: optionalUrl,
+ }).required(),
+ }
+
+ static auth = {
+ userKey: 'obs_user',
+ passKey: 'obs_pass',
+ serviceKey: 'obs',
+ isRequired: true,
+ }
+
+ static openApi = {
+ '/obs/{project}/{packageName}/{repository}/{arch}': {
+ get: {
+ summary: 'OBS package build status',
+ description:
+ '[Open Build Service](https://openbuildservice.org/) (OBS) is a generic system to build and distribute binary packages',
+ parameters: [
+ pathParam({ name: 'project', example: 'openSUSE:Tools' }),
+ pathParam({ name: 'packageName', example: 'osc' }),
+ pathParam({ name: 'repository', example: 'Debian_11' }),
+ pathParam({ name: 'arch', example: 'x86_64' }),
+ queryParam({ name: 'instance', example: 'https://api.opensuse.org' }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'build' }
+
+ static render({ status }) {
+ return renderBuildStatusBadge({ status })
+ }
+
+ async fetch({ instance, project, packageName, repository, arch }) {
+ return this._requestXml(
+ this.authHelper.withBasicAuth({
+ schema,
+ url: `${instance}/build/${project}/${repository}/${arch}/${packageName}/_status`,
+ parserOptions: {
+ ignoreAttributes: false,
+ },
+ }),
+ )
+ }
+
+ async handle(
+ { project, packageName, repository, arch },
+ { instance = 'https://api.opensuse.org' },
+ ) {
+ const resp = await this.fetch({
+ instance,
+ project,
+ packageName,
+ repository,
+ arch,
+ })
+ return this.constructor.render({
+ status: resp.status['@_code'],
+ })
+ }
+}
diff --git a/services/obs/obs.spec.js b/services/obs/obs.spec.js
new file mode 100644
index 0000000000000..fa19d9868b2f9
--- /dev/null
+++ b/services/obs/obs.spec.js
@@ -0,0 +1,16 @@
+import { testAuth } from '../test-helpers.js'
+import ObsService from './obs.service.js'
+
+describe('ObsService', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ ObsService,
+ 'BasicAuth',
+ `
+ `,
+ { contentType: 'application/xml' },
+ )
+ })
+ })
+})
diff --git a/services/obs/obs.tester.js b/services/obs/obs.tester.js
new file mode 100644
index 0000000000000..f05714f781212
--- /dev/null
+++ b/services/obs/obs.tester.js
@@ -0,0 +1,25 @@
+import { ServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import ObsService from './obs.service.js'
+import { isBuildStatus } from './obs-build-status.js'
+
+export const t = new ServiceTester({
+ id: 'obs',
+ title: 'openSUSE Open Build Service',
+})
+
+t.create('status (valid)')
+ .skipWhen(noToken(ObsService))
+ .get('/openSUSE:Factory/aaa_base/standard/x86_64.json?label=standard')
+ .expectBadge({
+ label: 'standard',
+ message: isBuildStatus,
+ })
+
+t.create('status (invalid)')
+ .skipWhen(noToken(ObsService))
+ .get('/home:sp1rit/this_package_will_never_exist/repo/arch.json')
+ .expectBadge({
+ label: 'build',
+ message: 'not found',
+ })
diff --git a/services/offset-earth/offset-earth-carbon-redirect.service.js b/services/offset-earth/offset-earth-carbon-redirect.service.js
new file mode 100644
index 0000000000000..8d107b7219259
--- /dev/null
+++ b/services/offset-earth/offset-earth-carbon-redirect.service.js
@@ -0,0 +1,16 @@
+import { retiredService } from '../index.js'
+
+export default [
+ // https://github.com/badges/shields/issues/5433
+ retiredService({
+ name: 'OffsetEarthCarbonRedirect',
+ category: 'other',
+ label: 'offset-earth',
+ route: {
+ base: 'offset-earth/carbon',
+ pattern: ':username',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+ }),
+]
diff --git a/services/offset-earth/offset-earth-carbon-redirect.tester.js b/services/offset-earth/offset-earth-carbon-redirect.tester.js
new file mode 100644
index 0000000000000..90cb551ce8102
--- /dev/null
+++ b/services/offset-earth/offset-earth-carbon-redirect.tester.js
@@ -0,0 +1,12 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'OffsetEarthCarbonRedirect',
+ title: 'Offset Earth (Carbon Offset) Redirector',
+ pathPrefix: '/offset-earth',
+})
+
+t.create('Offset Earth carbon alias').get('/carbon/ecologi.json').expectBadge({
+ label: 'offset-earth',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/offset-earth/offset-earth-carbon.service.js b/services/offset-earth/offset-earth-carbon.service.js
deleted file mode 100644
index 6be74160ecbc9..0000000000000
--- a/services/offset-earth/offset-earth-carbon.service.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const { BaseJsonService } = require('..')
-
-const apiSchema = Joi.object({
- total: Joi.number()
- .positive()
- .required(),
-}).required()
-
-module.exports = class OffsetEarthCarbonOffset extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'offset-earth/carbon',
- pattern: ':username',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Offset Earth (Carbon Offset)',
- namedParams: { username: 'offsetearth' },
- staticPreview: this.render({ count: 15.05 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'carbon offset' }
- }
-
- static render({ count }) {
- const tonnes = metric(count)
- return { message: `${tonnes} tonnes`, color: floorCount(count, 0.5, 1, 5) }
- }
-
- async fetch({ username }) {
- const url = `https://public.offset.earth/users/${username}/carbon-offset`
- return this._requestJson({
- url,
- schema: apiSchema,
- errorMessages: {
- 404: 'username not found',
- },
- })
- }
-
- async handle({ username }) {
- const { total } = await this.fetch({ username })
-
- return this.constructor.render({ count: total })
- }
-}
diff --git a/services/offset-earth/offset-earth-carbon.tester.js b/services/offset-earth/offset-earth-carbon.tester.js
deleted file mode 100644
index b7f066dc807f1..0000000000000
--- a/services/offset-earth/offset-earth-carbon.tester.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { withRegex } = require('../test-validators')
-
-t.create('request for existing username')
- .get('/offsetearth.json')
- .expectBadge({
- label: 'carbon offset',
- message: withRegex(/[\d.]+ tonnes/),
- })
-
-t.create('invalid username')
- .get('/non-existent-username.json')
- .expectBadge({
- label: 'carbon offset',
- message: 'username not found',
- color: 'red',
- })
diff --git a/services/offset-earth/offset-earth-trees-redirect.service.js b/services/offset-earth/offset-earth-trees-redirect.service.js
new file mode 100644
index 0000000000000..fa26187a57dda
--- /dev/null
+++ b/services/offset-earth/offset-earth-trees-redirect.service.js
@@ -0,0 +1,16 @@
+import { retiredService } from '../index.js'
+
+export default [
+ // https://github.com/badges/shields/issues/5433
+ retiredService({
+ name: 'OffsetEarthTreesRedirect',
+ category: 'other',
+ label: 'offset-earth',
+ route: {
+ base: 'offset-earth/trees',
+ pattern: ':username',
+ },
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
+ }),
+]
diff --git a/services/offset-earth/offset-earth-trees-redirect.tester.js b/services/offset-earth/offset-earth-trees-redirect.tester.js
new file mode 100644
index 0000000000000..8b4c8d24b4168
--- /dev/null
+++ b/services/offset-earth/offset-earth-trees-redirect.tester.js
@@ -0,0 +1,12 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'OffsetEarthTreesRedirect',
+ title: 'Offset Earth (Trees) Redirector',
+ pathPrefix: '/offset-earth',
+})
+
+t.create('Offset Earth trees alias').get('/trees/ecologi.json').expectBadge({
+ label: 'offset-earth',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/offset-earth/offset-earth-trees.service.js b/services/offset-earth/offset-earth-trees.service.js
deleted file mode 100644
index 876a9946ef12d..0000000000000
--- a/services/offset-earth/offset-earth-trees.service.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const apiSchema = Joi.object({
- total: nonNegativeInteger,
-}).required()
-
-module.exports = class OffsetEarthTrees extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'offset-earth/trees',
- pattern: ':username',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Offset Earth (Trees)',
- namedParams: { username: 'offsetearth' },
- staticPreview: this.render({ count: 250 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'trees' }
- }
-
- static render({ count }) {
- return { message: metric(count), color: floorCount(count, 10, 50, 100) }
- }
-
- async fetch({ username }) {
- const url = `https://public.offset.earth/users/${username}/trees`
- return this._requestJson({
- url,
- schema: apiSchema,
- errorMessages: {
- 404: 'username not found',
- },
- })
- }
-
- async handle({ username }) {
- const { total } = await this.fetch({ username })
-
- return this.constructor.render({ count: total })
- }
-}
diff --git a/services/offset-earth/offset-earth-trees.tester.js b/services/offset-earth/offset-earth-trees.tester.js
deleted file mode 100644
index 1b8537049f802..0000000000000
--- a/services/offset-earth/offset-earth-trees.tester.js
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
-
-t.create('request for existing username')
- .get('/offsetearth.json')
- .expectBadge({
- label: 'trees',
- message: isMetric,
- })
-
-t.create('request for existing username')
- .get('/offsetearth.json')
- .intercept(nock =>
- nock('https://public.offset.earth')
- .get('/users/offsetearth/trees')
- .reply(200, { total: 50 })
- )
- .expectBadge({
- label: 'trees',
- message: '50',
- color: 'green',
- })
-
-t.create('invalid username')
- .get('/non-existent-username.json')
- .expectBadge({ label: 'trees', message: 'username not found', color: 'red' })
diff --git a/services/open-vsx/open-vsx-base.js b/services/open-vsx/open-vsx-base.js
new file mode 100644
index 0000000000000..9a6aced1e9bb3
--- /dev/null
+++ b/services/open-vsx/open-vsx-base.js
@@ -0,0 +1,41 @@
+import Joi from 'joi'
+import { optionalNonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
+
+const extensionQuerySchema = Joi.object({
+ error: Joi.string(),
+ version: Joi.string().required(),
+ timestamp: Joi.string().isoDate().required(),
+ downloadCount: optionalNonNegativeInteger,
+ reviewCount: optionalNonNegativeInteger,
+ averageRating: Joi.number().when('reviewCount', {
+ is: Joi.exist(),
+ then: Joi.number().max(5),
+ otherwise: Joi.optional(),
+ }),
+}).required()
+
+export default class OpenVSXBase extends BaseJsonService {
+ static defaultBadgeData = {
+ label: 'open vsx',
+ color: 'blue',
+ }
+
+ async fetch({ namespace, extension, version }) {
+ return this._requestJson({
+ schema: extensionQuerySchema,
+ url: `https://open-vsx.org/api/${namespace}/${extension}${
+ version ? `/${version}` : ''
+ }`,
+ httpErrors: {
+ 400: 'invalid extension id',
+ 404: 'extension not found',
+ },
+ })
+ }
+}
+
+const description =
+ '[Open VSX](https://open-vsx.org/) (OVSX) is a registry of extensions for VS Code compatible editors.'
+
+export { OpenVSXBase, description }
diff --git a/services/open-vsx/open-vsx-downloads.service.js b/services/open-vsx/open-vsx-downloads.service.js
new file mode 100644
index 0000000000000..2a7f9a1d6385e
--- /dev/null
+++ b/services/open-vsx/open-vsx-downloads.service.js
@@ -0,0 +1,65 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { OpenVSXBase, description } from './open-vsx-base.js'
+
+export default class OpenVSXDownloads extends OpenVSXBase {
+ static category = 'downloads'
+
+ static route = {
+ base: 'open-vsx',
+ pattern: ':interval(dt)/:namespace/:extension/:version*',
+ }
+
+ static openApi = {
+ '/open-vsx/dt/{namespace}/{extension}': {
+ get: {
+ summary: 'Open VSX Downloads',
+ description,
+ parameters: pathParams(
+ {
+ name: 'namespace',
+ example: 'redhat',
+ },
+ {
+ name: 'extension',
+ example: 'java',
+ },
+ ),
+ },
+ },
+ '/open-vsx/dt/{namespace}/{extension}/{version}': {
+ get: {
+ summary: 'Open VSX Downloads (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'namespace',
+ example: 'redhat',
+ },
+ {
+ name: 'extension',
+ example: 'java',
+ },
+ {
+ name: 'version',
+ example: '0.69.0',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ namespace, extension, version }) {
+ const { version: tag, downloadCount: downloads } = await this.fetch({
+ namespace,
+ extension,
+ version,
+ })
+ return renderDownloadsBadge({
+ downloads,
+ version: version ? tag : undefined,
+ })
+ }
+}
diff --git a/services/open-vsx/open-vsx-downloads.tester.js b/services/open-vsx/open-vsx-downloads.tester.js
new file mode 100644
index 0000000000000..72da704337d0f
--- /dev/null
+++ b/services/open-vsx/open-vsx-downloads.tester.js
@@ -0,0 +1,22 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex, isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+const isVersionLabel = withRegex(/^downloads@(\d+\.\d+\.\d+)(\.\d+)?$/)
+
+t.create('downloads invalid extension')
+ .get('/dt/badges/shields.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'extension not found',
+ })
+
+t.create('downloads').get('/dt/redhat/java.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('downloads version').get('/dt/redhat/java/latest.json').expectBadge({
+ label: isVersionLabel,
+ message: isMetric,
+})
diff --git a/services/open-vsx/open-vsx-rating.service.js b/services/open-vsx/open-vsx-rating.service.js
new file mode 100644
index 0000000000000..6abeafc0ee784
--- /dev/null
+++ b/services/open-vsx/open-vsx-rating.service.js
@@ -0,0 +1,72 @@
+import { pathParams } from '../index.js'
+import { starRating } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { OpenVSXBase, description } from './open-vsx-base.js'
+
+export default class OpenVSXRating extends OpenVSXBase {
+ static category = 'rating'
+
+ static route = {
+ base: 'open-vsx',
+ pattern: ':format(rating|stars)/:namespace/:extension',
+ }
+
+ static openApi = {
+ '/open-vsx/{format}/{namespace}/{extension}': {
+ get: {
+ summary: 'Open VSX Rating',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ example: 'rating',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ },
+ {
+ name: 'namespace',
+ example: 'redhat',
+ },
+ {
+ name: 'extension',
+ example: 'java',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'rating',
+ }
+
+ static render({ format, averageRating, ratingCount }) {
+ if (ratingCount === 0) {
+ return {
+ message: 'unrated',
+ color: 'lightgrey',
+ }
+ }
+
+ const message =
+ format === 'rating'
+ ? `${averageRating.toFixed(1)}/5 (${ratingCount})`
+ : starRating(averageRating)
+
+ return {
+ message,
+ color: floorCount(averageRating, 2, 3, 4),
+ }
+ }
+
+ async handle({ format, namespace, extension }) {
+ const { averageRating, reviewCount } = await this.fetch({
+ namespace,
+ extension,
+ })
+ return this.constructor.render({
+ format,
+ averageRating,
+ ratingCount: reviewCount,
+ })
+ }
+}
diff --git a/services/open-vsx/open-vsx-rating.spec.js b/services/open-vsx/open-vsx-rating.spec.js
new file mode 100644
index 0000000000000..8dc1ec0c286aa
--- /dev/null
+++ b/services/open-vsx/open-vsx-rating.spec.js
@@ -0,0 +1,11 @@
+import { test, given } from 'sazerac'
+import OpenVSXRating from './open-vsx-rating.service.js'
+
+describe('OpenVSXRating', function () {
+ test(OpenVSXRating.render, () => {
+ given({ ratingCount: 0 }).expect({
+ message: 'unrated',
+ color: 'lightgrey',
+ })
+ })
+})
diff --git a/services/open-vsx/open-vsx-rating.tester.js b/services/open-vsx/open-vsx-rating.tester.js
new file mode 100644
index 0000000000000..a007432b78bb4
--- /dev/null
+++ b/services/open-vsx/open-vsx-rating.tester.js
@@ -0,0 +1,29 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex, isStarRating } from '../test-validators.js'
+export const t = await createServiceTester()
+
+const isVscodeRating = withRegex(/[0-5]\.[0-9]{1}\/5?\s*\([0-9]*\)$/)
+
+t.create('rating invalid extension')
+ .get('/rating/badges/shields.json')
+ .expectBadge({
+ label: 'rating',
+ message: 'extension not found',
+ })
+
+t.create('rating').get('/rating/redhat/java.json').expectBadge({
+ label: 'rating',
+ message: isVscodeRating,
+})
+
+t.create('stars invalid extension')
+ .get('/stars/badges/shields.json')
+ .expectBadge({
+ label: 'rating',
+ message: 'extension not found',
+ })
+
+t.create('stars').get('/stars/redhat/java.json').expectBadge({
+ label: 'rating',
+ message: isStarRating,
+})
diff --git a/services/open-vsx/open-vsx-release-date.service.js b/services/open-vsx/open-vsx-release-date.service.js
new file mode 100644
index 0000000000000..511c33de1aeb5
--- /dev/null
+++ b/services/open-vsx/open-vsx-release-date.service.js
@@ -0,0 +1,38 @@
+import { pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import { OpenVSXBase, description } from './open-vsx-base.js'
+
+export default class OpenVSXReleaseDate extends OpenVSXBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'open-vsx',
+ pattern: 'release-date/:namespace/:extension',
+ }
+
+ static openApi = {
+ '/open-vsx/release-date/{namespace}/{extension}': {
+ get: {
+ summary: 'Open VSX Release Date',
+ description,
+ parameters: pathParams(
+ {
+ name: 'namespace',
+ example: 'redhat',
+ },
+ {
+ name: 'extension',
+ example: 'java',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'release date' }
+
+ async handle({ namespace, extension }) {
+ const { timestamp } = await this.fetch({ namespace, extension })
+ return renderDateBadge(timestamp)
+ }
+}
diff --git a/services/open-vsx/open-vsx-release-date.tester.js b/services/open-vsx/open-vsx-release-date.tester.js
new file mode 100644
index 0000000000000..2f3649d3ad60e
--- /dev/null
+++ b/services/open-vsx/open-vsx-release-date.tester.js
@@ -0,0 +1,15 @@
+import { createServiceTester } from '../tester.js'
+import { isFormattedDate } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('release date invalid extension')
+ .get('/release-date/badges/shields.json')
+ .expectBadge({
+ label: 'release date',
+ message: 'extension not found',
+ })
+
+t.create('release date').get('/release-date/redhat/java.json').expectBadge({
+ label: 'release date',
+ message: isFormattedDate,
+})
diff --git a/services/open-vsx/open-vsx-version.service.js b/services/open-vsx/open-vsx-version.service.js
new file mode 100644
index 0000000000000..1385e93887d2a
--- /dev/null
+++ b/services/open-vsx/open-vsx-version.service.js
@@ -0,0 +1,40 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { OpenVSXBase, description } from './open-vsx-base.js'
+
+export default class OpenVSXVersion extends OpenVSXBase {
+ static category = 'version'
+
+ static route = {
+ base: 'open-vsx',
+ pattern: 'v/:namespace/:extension',
+ }
+
+ static openApi = {
+ '/open-vsx/v/{namespace}/{extension}': {
+ get: {
+ summary: 'Open VSX Version',
+ description,
+ parameters: pathParams(
+ {
+ name: 'namespace',
+ example: 'redhat',
+ },
+ {
+ name: 'extension',
+ example: 'java',
+ },
+ ),
+ },
+ },
+ }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async handle({ namespace, extension }) {
+ const { version } = await this.fetch({ namespace, extension })
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/open-vsx/open-vsx-version.tester.js b/services/open-vsx/open-vsx-version.tester.js
new file mode 100644
index 0000000000000..ee3bc1a18a161
--- /dev/null
+++ b/services/open-vsx/open-vsx-version.tester.js
@@ -0,0 +1,17 @@
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+export const t = await createServiceTester()
+
+const isVersion = withRegex(/^v(\d+\.\d+\.\d+)(\.\d+)?$/)
+
+t.create('version invalid extension')
+ .get('/v/badges/shields.json')
+ .expectBadge({
+ label: 'open vsx',
+ message: 'extension not found',
+ })
+
+t.create('version').get('/v/redhat/java.json').expectBadge({
+ label: 'open vsx',
+ message: isVersion,
+})
diff --git a/services/opencollective/opencollective-all.service.js b/services/opencollective/opencollective-all.service.js
index d8af9c0e33e31..174ef58fd71be 100644
--- a/services/opencollective/opencollective-all.service.js
+++ b/services/opencollective/opencollective-all.service.js
@@ -1,31 +1,33 @@
-'use strict'
+import { pathParams } from '../index.js'
+import OpencollectiveBase from './opencollective-base.js'
-const OpencollectiveBase = require('./opencollective-base')
+export default class OpencollectiveAll extends OpencollectiveBase {
+ static route = this.buildRoute('all')
-module.exports = class OpencollectiveAll extends OpencollectiveBase {
- static get route() {
- return this.buildRoute('all')
- }
-
- static get examples() {
- return [
- {
- title: 'Open Collective backers and sponsors',
- namedParams: { collective: 'shields' },
- staticPreview: this.render(35),
- keywords: ['opencollective'],
+ static openApi = {
+ '/opencollective/all/{collective}': {
+ get: {
+ summary: 'Open Collective backers and sponsors',
+ parameters: pathParams({
+ name: 'collective',
+ example: 'shields',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'backers and sponsors',
- }
+ static _cacheLength = 3600
+
+ static defaultBadgeData = {
+ label: 'backers and sponsors',
}
async handle({ collective }) {
- const { backersCount } = await this.fetchCollectiveInfo(collective)
+ const data = await this.fetchCollectiveInfo({
+ collective,
+ accountType: [],
+ })
+ const backersCount = this.getCount(data)
return this.constructor.render(backersCount)
}
}
diff --git a/services/opencollective/opencollective-all.tester.js b/services/opencollective/opencollective-all.tester.js
index c4584349f92e4..61f2ef3e2cabb 100644
--- a/services/opencollective/opencollective-all.tester.js
+++ b/services/opencollective/opencollective-all.tester.js
@@ -1,29 +1,7 @@
-'use strict'
+import { nonNegativeInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('renders correctly')
- .get('/shields.json')
- .intercept(nock =>
- nock('https://opencollective.com/')
- .get('/shields.json')
- .reply(200, {
- slug: 'shields',
- currency: 'USD',
- image:
- 'https://opencollective-production.s3-us-west-1.amazonaws.com/44dcbb90-1ee9-11e8-a4c3-7bb1885c0b6e.png',
- balance: 105494,
- yearlyIncome: 157371,
- backersCount: 35,
- contributorsCount: 276,
- })
- )
- .expectBadge({
- label: 'backers and sponsors',
- message: '35',
- color: 'brightgreen',
- })
t.create('gets amount of backers and sponsors')
.get('/shields.json')
.expectBadge({
@@ -31,23 +9,10 @@ t.create('gets amount of backers and sponsors')
message: nonNegativeInteger,
})
-t.create('renders not found correctly')
- .get('/nonexistent-collective.json')
- .intercept(nock =>
- nock('https://opencollective.com/')
- .get('/nonexistent-collective.json')
- .reply(404, 'Not found')
- )
- .expectBadge({
- label: 'backers and sponsors',
- message: 'collective not found',
- color: 'red',
- })
-
t.create('handles not found correctly')
.get('/nonexistent-collective.json')
.expectBadge({
label: 'backers and sponsors',
- message: 'collective not found',
- color: 'red',
+ message: 'No collective found with slug nonexistent-collective',
+ color: 'lightgrey',
})
diff --git a/services/opencollective/opencollective-backers.service.js b/services/opencollective/opencollective-backers.service.js
index a9cb33ecc8019..0565e097bb385 100644
--- a/services/opencollective/opencollective-backers.service.js
+++ b/services/opencollective/opencollective-backers.service.js
@@ -1,34 +1,34 @@
-'use strict'
+import { pathParams } from '../index.js'
+import OpencollectiveBase from './opencollective-base.js'
-const OpencollectiveBase = require('./opencollective-base')
+export default class OpencollectiveBackers extends OpencollectiveBase {
+ static route = this.buildRoute('backers')
-module.exports = class OpencollectiveBackers extends OpencollectiveBase {
- static get route() {
- return this.buildRoute('backers')
- }
-
- static get examples() {
- return [
- {
- title: 'Open Collective backers',
- namedParams: { collective: 'shields' },
- staticPreview: this.render(25),
- keywords: ['opencollective'],
+ static openApi = {
+ '/opencollective/backers/{collective}': {
+ get: {
+ summary: 'Open Collective backers',
+ parameters: pathParams({
+ name: 'collective',
+ example: 'shields',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'backers',
- }
+ static _cacheLength = 3600
+
+ static defaultBadgeData = {
+ label: 'backers',
}
async handle({ collective }) {
- const { backersCount } = await this.fetchCollectiveBackersCount(
+ const data = await this.fetchCollectiveInfo({
collective,
- { userType: 'users' }
- )
+ accountType: ['INDIVIDUAL'],
+ })
+ const backersCount = this.getCount(data)
+
return this.constructor.render(backersCount)
}
}
diff --git a/services/opencollective/opencollective-backers.tester.js b/services/opencollective/opencollective-backers.tester.js
index fbdaf1bfbef76..29855ae57d07c 100644
--- a/services/opencollective/opencollective-backers.tester.js
+++ b/services/opencollective/opencollective-backers.tester.js
@@ -1,93 +1,16 @@
-'use strict'
+import { nonNegativeInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('renders correctly')
- .get('/shields.json')
- .intercept(nock =>
- nock('https://opencollective.com/')
- .get('/shields/members/users.json')
- .reply(200, [
- { MemberId: 8685, type: 'USER', role: 'ADMIN' },
- { MemberId: 8686, type: 'USER', role: 'ADMIN' },
- { MemberId: 8682, type: 'USER', role: 'ADMIN' },
- { MemberId: 10305, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 10396, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 10733, type: 'USER', role: 'BACKER' },
- { MemberId: 8684, type: 'USER', role: 'ADMIN' },
- { MemberId: 10741, type: 'USER', role: 'BACKER' },
- {
- MemberId: 10756,
- type: 'USER',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 11578, type: 'USER', role: 'CONTRIBUTOR' },
- { MemberId: 13459, type: 'USER', role: 'CONTRIBUTOR' },
- {
- MemberId: 13507,
- type: 'USER',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 13512, type: 'USER', role: 'BACKER' },
- { MemberId: 13513, type: 'USER', role: 'FUNDRAISER' },
- { MemberId: 13984, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 14916, type: 'USER', role: 'BACKER' },
- {
- MemberId: 16326,
- type: 'USER',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 18252, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 17631, type: 'USER', role: 'BACKER', tier: 'backer' },
- {
- MemberId: 16420,
- type: 'USER',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 17186, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 18791, type: 'USER', role: 'BACKER', tier: 'backer' },
- {
- MemberId: 19279,
- type: 'USER',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 19863, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 21451, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 22718, type: 'USER', role: 'BACKER' },
- { MemberId: 23561, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 25092, type: 'USER', role: 'CONTRIBUTOR' },
- { MemberId: 24473, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 25439, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 24483, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 25090, type: 'USER', role: 'CONTRIBUTOR' },
- { MemberId: 26404, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 27026, type: 'USER', role: 'BACKER', tier: 'backer' },
- { MemberId: 27132, type: 'USER', role: 'CONTRIBUTOR' },
- ])
- )
- .expectBadge({
- label: 'backers',
- message: '25',
- color: 'brightgreen',
- })
-
-t.create('gets amount of backers')
- .get('/shields.json')
- .expectBadge({
- label: 'backers',
- message: nonNegativeInteger,
- })
+t.create('gets amount of backers').get('/shields.json').expectBadge({
+ label: 'backers',
+ message: nonNegativeInteger,
+})
t.create('handles not found correctly')
.get('/nonexistent-collective.json')
.expectBadge({
label: 'backers',
- message: 'collective not found',
- color: 'red',
+ message: 'No collective found with slug nonexistent-collective',
+ color: 'lightgrey',
})
diff --git a/services/opencollective/opencollective-base.js b/services/opencollective/opencollective-base.js
index 4ccd303c29bc7..97e42da30d58a 100644
--- a/services/opencollective/opencollective-base.js
+++ b/services/opencollective/opencollective-base.js
@@ -1,29 +1,36 @@
-'use strict'
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import { BaseGraphqlService } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+const schema = Joi.object({
+ data: Joi.object({
+ account: Joi.object({
+ name: Joi.string(),
+ slug: Joi.string(),
+ members: Joi.object({
+ totalCount: nonNegativeInteger,
+ nodes: Joi.array().items(
+ Joi.object({
+ tier: Joi.object({
+ legacyId: Joi.number(),
+ name: Joi.string(),
+ }).allow(null),
+ }),
+ ),
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
-// https://developer.opencollective.com/#/api/collectives?id=get-info
-const collectiveDetailsSchema = Joi.object().keys({
- slug: Joi.string().required(),
- backersCount: nonNegativeInteger,
-})
+export default class OpencollectiveBase extends BaseGraphqlService {
+ static category = 'funding'
-// https://developer.opencollective.com/#/api/collectives?id=get-members
-function buildMembersArraySchema({ userType, tierRequired }) {
- const keys = {
- MemberId: Joi.number().required(),
- type: userType || Joi.string().required(),
- role: Joi.string().required(),
- }
- if (tierRequired) keys.tier = Joi.string().required()
- return Joi.array().items(Joi.object().keys(keys))
-}
-
-module.exports = class OpencollectiveBase extends BaseJsonService {
- static get category() {
- return 'funding'
+ static auth = {
+ passKey: 'opencollective_token',
+ authorizedOrigins: ['https://api.opencollective.com'],
+ isRequired: false,
}
static buildRoute(base, withTierId) {
@@ -34,52 +41,58 @@ module.exports = class OpencollectiveBase extends BaseJsonService {
}
static render(backersCount, label) {
- const badge = {
- message: backersCount,
+ return {
+ label,
+ message: metric(backersCount),
color: backersCount > 0 ? 'brightgreen' : 'lightgrey',
}
- if (label) badge.label = label
- return badge
}
- async fetchCollectiveInfo(collective) {
- return this._requestJson({
- schema: collectiveDetailsSchema,
- // https://developer.opencollective.com/#/api/collectives?id=get-info
- url: `https://opencollective.com/${collective}.json`,
- errorMessages: {
- 404: 'collective not found',
- },
- })
+ async fetchCollectiveInfo({ collective, accountType }) {
+ return this._requestGraphql(
+ this.authHelper.withQueryStringAuth(
+ { passKey: 'personalToken' },
+ {
+ schema,
+ url: 'https://api.opencollective.com/graphql/v2',
+ query: gql`
+ query account($slug: String, $accountType: [AccountType]) {
+ account(slug: $slug) {
+ name
+ slug
+ members(accountType: $accountType, role: BACKER) {
+ totalCount
+ nodes {
+ tier {
+ legacyId
+ name
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ slug: collective,
+ accountType,
+ },
+ options: {
+ headers: { 'content-type': 'application/json' },
+ },
+ },
+ ),
+ )
}
- async fetchCollectiveBackersCount(collective, { userType, tierId }) {
- const schema = buildMembersArraySchema({
- userType:
- userType === 'users'
- ? 'USER'
- : userType === 'organizations'
- ? 'ORGANIZATION'
- : undefined,
- tierRequired: tierId,
- })
- const members = await this._requestJson({
- schema,
- // https://developer.opencollective.com/#/api/collectives?id=get-members
- // https://developer.opencollective.com/#/api/collectives?id=get-members-per-tier
- url: `https://opencollective.com/${collective}/members/${userType ||
- 'all'}.json${tierId ? `?TierId=${tierId}` : ''}`,
- errorMessages: {
- 404: 'collective not found',
+ getCount(data) {
+ const {
+ data: {
+ account: {
+ members: { totalCount },
+ },
},
- })
+ } = data
- const result = {
- backersCount: members.filter(member => member.role === 'BACKER').length,
- }
- // Find the title of the tier
- if (tierId && members.length > 0)
- result.tier = members.map(member => member.tier)[0]
- return result
+ return totalCount
}
}
diff --git a/services/opencollective/opencollective-base.spec.js b/services/opencollective/opencollective-base.spec.js
new file mode 100644
index 0000000000000..87a6646f2cd31
--- /dev/null
+++ b/services/opencollective/opencollective-base.spec.js
@@ -0,0 +1,37 @@
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import OpencollectiveBase from './opencollective-base.js'
+
+class DummyOpencollectiveService extends OpencollectiveBase {
+ static route = this.buildRoute('dummy')
+
+ async handle({ collective }) {
+ const data = await this.fetchCollectiveInfo({
+ collective,
+ accountType: [],
+ })
+ return this.constructor.render(this.getCount(data))
+ }
+}
+
+describe('OpencollectiveBase', function () {
+ describe('auth', function () {
+ cleanUpNockAfterEach()
+
+ const config = { private: { opencollective_token: 'fake-token' } }
+
+ it('sends the auth information as configured', async function () {
+ const scope = nock('https://api.opencollective.com')
+ .post('/graphql/v2')
+ .query({ personalToken: 'fake-token' })
+ .reply(200, { data: { account: { members: { totalCount: 1 } } } })
+
+ expect(
+ await DummyOpencollectiveService.invoke(defaultContext, config, {}),
+ ).to.deep.equal({ color: 'brightgreen', label: undefined, message: '1' })
+
+ scope.done()
+ })
+ })
+})
diff --git a/services/opencollective/opencollective-by-tier.service.js b/services/opencollective/opencollective-by-tier.service.js
index f3055d9486b75..7f265b28c5542 100644
--- a/services/opencollective/opencollective-by-tier.service.js
+++ b/services/opencollective/opencollective-by-tier.service.js
@@ -1,33 +1,116 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
-const OpencollectiveBase = require('./opencollective-base')
-
-const documentation = `How to get the tierId
+const description = `How to get the tierId
According to open collectives documentation , you can find the tierId by looking at the URL after clicking on a Tier Card on the collective page. (e.g. tierId for https://opencollective.com/shields/order/2988 is 2988)
`
-module.exports = class OpencollectiveByTier extends OpencollectiveBase {
- static get route() {
- return this.buildRoute('tier', true)
+// https://developer.opencollective.com/#/api/collectives?id=get-info
+const collectiveDetailsSchema = Joi.object().keys({
+ slug: Joi.string().required(),
+ backersCount: nonNegativeInteger,
+})
+
+// https://developer.opencollective.com/#/api/collectives?id=get-members
+function buildMembersArraySchema({ userType, tierRequired }) {
+ const keys = {
+ MemberId: Joi.number().required(),
+ type: userType || Joi.string().required(),
+ role: Joi.string().required(),
}
+ if (tierRequired) keys.tier = Joi.string().required()
+ return Joi.array().items(Joi.object().keys(keys))
+}
- static get examples() {
- return [
- {
- title: 'Open Collective members by tier',
- namedParams: { collective: 'shields', tierId: '2988' },
- staticPreview: this.render(8, 'monthly backers'),
- keywords: ['opencollective'],
- documentation,
- },
- ]
+class OpencollectiveBaseJson extends BaseJsonService {
+ static category = 'funding'
+
+ static buildRoute(base, withTierId) {
+ return {
+ base: `opencollective${base ? `/${base}` : ''}`,
+ pattern: `:collective${withTierId ? '/:tierId' : ''}`,
+ }
}
- static get defaultBadgeData() {
+ static render(backersCount, label) {
return {
- label: 'open collective',
+ label,
+ message: metric(backersCount),
+ color: backersCount > 0 ? 'brightgreen' : 'lightgrey',
}
}
+ async fetchCollectiveInfo(collective) {
+ return this._requestJson({
+ schema: collectiveDetailsSchema,
+ // https://developer.opencollective.com/#/api/collectives?id=get-info
+ url: `https://opencollective.com/${collective}.json`,
+ httpErrors: {
+ 404: 'collective not found',
+ },
+ })
+ }
+
+ async fetchCollectiveBackersCount(collective, { userType, tierId }) {
+ const schema = buildMembersArraySchema({
+ userType:
+ userType === 'users'
+ ? 'USER'
+ : userType === 'organizations'
+ ? 'ORGANIZATION'
+ : undefined,
+ tierRequired: tierId,
+ })
+ const members = await this._requestJson({
+ schema,
+ // https://developer.opencollective.com/#/api/collectives?id=get-members
+ // https://developer.opencollective.com/#/api/collectives?id=get-members-per-tier
+ url: `https://opencollective.com/${collective}/members/${
+ userType || 'all'
+ }.json${tierId ? `?TierId=${tierId}` : ''}`,
+ httpErrors: {
+ 404: 'collective not found',
+ },
+ })
+
+ const result = {
+ backersCount: members.filter(member => member.role === 'BACKER').length,
+ }
+ // Find the title of the tier
+ if (tierId && members.length > 0)
+ result.tier = members.map(member => member.tier)[0]
+ return result
+ }
+}
+
+// TODO: 1. pagination is needed. 2. use new graphql api instead of legacy rest api
+export default class OpencollectiveByTier extends OpencollectiveBaseJson {
+ static route = this.buildRoute('tier', true)
+
+ static openApi = {
+ '/opencollective/tier/{collective}/{tierId}': {
+ get: {
+ summary: 'Open Collective members by tier',
+ description,
+ parameters: pathParams(
+ {
+ name: 'collective',
+ example: 'shields',
+ },
+ {
+ name: 'tierId',
+ example: '2988',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'open collective',
+ }
+
async handle({ collective, tierId }) {
const result = await this.fetchCollectiveBackersCount(collective, {
tierId,
diff --git a/services/opencollective/opencollective-by-tier.tester.js b/services/opencollective/opencollective-by-tier.tester.js
index d2b53a8d4e03e..0b5bb68c5427d 100644
--- a/services/opencollective/opencollective-by-tier.tester.js
+++ b/services/opencollective/opencollective-by-tier.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { nonNegativeInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('renders correctly')
.get('/shields/2988.json')
@@ -57,7 +56,7 @@ t.create('renders correctly')
role: 'BACKER',
tier: 'monthly backer',
},
- ])
+ ]),
)
.expectBadge({
label: 'monthly backers',
@@ -71,7 +70,7 @@ t.create('shows 0 when given a non existent tier')
.intercept(nock =>
nock('https://opencollective.com/')
.get('/shields/members/all.json?TierId=1234567890')
- .reply(200, [])
+ .reply(200, []),
)
.expectBadge({
label: 'new tier',
diff --git a/services/opencollective/opencollective-sponsors.service.js b/services/opencollective/opencollective-sponsors.service.js
index 6d2202ff11e4b..192d5f4bef420 100644
--- a/services/opencollective/opencollective-sponsors.service.js
+++ b/services/opencollective/opencollective-sponsors.service.js
@@ -1,34 +1,33 @@
-'use strict'
+import { pathParams } from '../index.js'
+import OpencollectiveBase from './opencollective-base.js'
-const OpencollectiveBase = require('./opencollective-base')
+export default class OpencollectiveSponsors extends OpencollectiveBase {
+ static route = this.buildRoute('sponsors')
-module.exports = class OpencollectiveSponsors extends OpencollectiveBase {
- static get route() {
- return this.buildRoute('sponsors')
- }
-
- static get examples() {
- return [
- {
- title: 'Open Collective sponsors',
- namedParams: { collective: 'shields' },
- staticPreview: this.render(10),
- keywords: ['opencollective'],
+ static openApi = {
+ '/opencollective/sponsors/{collective}': {
+ get: {
+ summary: 'Open Collective sponsors',
+ parameters: pathParams({
+ name: 'collective',
+ example: 'shields',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'sponsors',
- }
+ static _cacheLength = 3600
+
+ static defaultBadgeData = {
+ label: 'sponsors',
}
async handle({ collective }) {
- const { backersCount } = await this.fetchCollectiveBackersCount(
+ const data = await this.fetchCollectiveInfo({
collective,
- { userType: 'organizations' }
- )
+ accountType: ['ORGANIZATION'],
+ })
+ const backersCount = this.getCount(data)
return this.constructor.render(backersCount)
}
}
diff --git a/services/opencollective/opencollective-sponsors.tester.js b/services/opencollective/opencollective-sponsors.tester.js
index 68d740f5c9b86..d563744a49ae3 100644
--- a/services/opencollective/opencollective-sponsors.tester.js
+++ b/services/opencollective/opencollective-sponsors.tester.js
@@ -1,84 +1,17 @@
-'use strict'
+import { nonNegativeInteger } from '../validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { nonNegativeInteger } = require('../validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('renders correctly')
- .get('/shields.json')
- .intercept(nock =>
- nock('https://opencollective.com/')
- .get('/shields/members/organizations.json')
- .reply(200, [
- { MemberId: 8683, type: 'ORGANIZATION', role: 'HOST' },
- {
- MemberId: 13484,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'backer',
- },
- { MemberId: 13508, type: 'ORGANIZATION', role: 'FUNDRAISER' },
- { MemberId: 15987, type: 'ORGANIZATION', role: 'BACKER' },
- {
- MemberId: 16561,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'sponsor',
- },
- {
- MemberId: 16469,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'sponsor',
- },
- {
- MemberId: 18162,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'sponsor',
- },
- {
- MemberId: 21023,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'sponsor',
- },
- {
- MemberId: 21482,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- {
- MemberId: 26367,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- { MemberId: 27531, type: 'ORGANIZATION', role: 'BACKER' },
- {
- MemberId: 29443,
- type: 'ORGANIZATION',
- role: 'BACKER',
- tier: 'monthly backer',
- },
- ])
- )
- .expectBadge({
- label: 'sponsors',
- message: '10',
- color: 'brightgreen',
- })
-t.create('gets amount of sponsors')
- .get('/shields.json')
- .expectBadge({
- label: 'sponsors',
- message: nonNegativeInteger,
- })
+t.create('gets amount of sponsors').get('/shields.json').expectBadge({
+ label: 'sponsors',
+ message: nonNegativeInteger,
+ color: 'brightgreen',
+})
t.create('handles not found correctly')
.get('/nonexistent-collective.json')
.expectBadge({
label: 'sponsors',
- message: 'collective not found',
- color: 'red',
+ message: 'No collective found with slug nonexistent-collective',
+ color: 'lightgrey',
})
diff --git a/services/opm/opm-version.service.js b/services/opm/opm-version.service.js
index 9644a60201ff7..03d66950b66c0 100644
--- a/services/opm/opm-version.service.js
+++ b/services/opm/opm-version.service.js
@@ -1,56 +1,53 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { BaseService, NotFound, InvalidResponse, pathParams } from '../index.js'
-const { renderVersionBadge } = require('../version')
-const { BaseService, NotFound, InvalidResponse } = require('..')
+export default class OpmVersion extends BaseService {
+ static category = 'version'
-module.exports = class OpmVersion extends BaseService {
- static get category() {
- return 'version'
+ static route = {
+ base: 'opm/v',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'opm/v',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'OPM',
- namedParams: {
- user: 'openresty',
- moduleName: 'lua-resty-lrucache',
- },
- staticPreview: renderVersionBadge({ version: 'v0.08' }),
+ static openApi = {
+ '/opm/v/{user}/{moduleName}': {
+ get: {
+ summary: 'OPM Version',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'openresty',
+ },
+ {
+ name: 'moduleName',
+ example: 'lua-resty-lrucache',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'opm',
- }
+ static defaultBadgeData = {
+ label: 'opm',
}
async fetch({ user, moduleName }) {
const { res } = await this._request({
- url: `https://opm.openresty.org/api/pkg/fetch`,
+ url: 'https://opm.openresty.org/api/pkg/fetch',
options: {
method: 'HEAD',
- qs: {
+ searchParams: {
account: user,
name: moduleName,
},
},
- errorMessages: {
+ httpErrors: {
404: 'module not found',
},
})
- // XXX: intercept 302 redirects and set followRedirect to false
- const location = res.request.path
+ // TODO: set followRedirect to false and intercept 302 redirects
+ const location = res.redirectUrls[0].toString()
if (!location) {
throw new NotFound({ prettyMessage: 'module not found' })
}
diff --git a/services/opm/opm-version.tester.js b/services/opm/opm-version.tester.js
index 18e9c2cef0d8f..c0e187cf3491a 100644
--- a/services/opm/opm-version.tester.js
+++ b/services/opm/opm-version.tester.js
@@ -1,18 +1,15 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isValidVersion = Joi.string()
.regex(/^v[\d+.]+$/)
.required()
-t.create('version')
- .get('/openresty/lua-resty-lrucache.json')
- .expectBadge({
- label: 'opm',
- message: isValidVersion,
- })
+t.create('version').get('/openresty/lua-resty-lrucache.json').expectBadge({
+ label: 'opm',
+ message: isValidVersion,
+})
t.create('unknown module')
.get('/openresty/does-not-exist.json')
diff --git a/services/ore/ore-base.js b/services/ore/ore-base.js
new file mode 100644
index 0000000000000..a2388c992124d
--- /dev/null
+++ b/services/ore/ore-base.js
@@ -0,0 +1,85 @@
+import Joi from 'joi'
+import { BaseJsonService, InvalidResponse } from '../index.js'
+
+const sessionSchema = Joi.object({
+ session: Joi.string().required(),
+}).required()
+
+const resourceSchema = Joi.object({
+ promoted_versions: Joi.array()
+ .items({
+ version: Joi.string().required(),
+ tags: Joi.array()
+ .items({
+ name: Joi.string().required(),
+ display_data: Joi.string().allow(null),
+ })
+ .required(),
+ })
+ .required(),
+
+ stats: Joi.object({
+ stars: Joi.number().required(),
+ downloads: Joi.number().required(),
+ }).required(),
+
+ category: Joi.string().required(),
+ settings: Joi.object({
+ license: Joi.object({
+ name: Joi.string().allow(null).allow(''),
+ url: Joi.string().allow(null).allow(''),
+ }),
+ }).required(),
+}).required()
+
+const description = `
+Ore is a Minecraft package repository.
+Your Plugin ID is the name of your plugin in lowercase, without any spaces or dashes.
+Example: https://ore.spongepowered.org/Erigitic/Total-Economy - Here the Plugin ID is totaleconomy.
`
+
+class BaseOreService extends BaseJsonService {
+ async _refreshSessionToken() {
+ const requestOptions = {
+ schema: sessionSchema,
+ url: 'https://ore.spongepowered.org/api/v2/authenticate',
+ options: { method: 'POST' },
+ }
+ const { session } = await this._requestJson(requestOptions)
+ BaseOreService.sessionToken = session
+ }
+
+ async fetch({
+ pluginId,
+ schema = resourceSchema,
+ url = `https://ore.spongepowered.org/api/v2/projects/${pluginId}`,
+ }) {
+ const requestOptions = {
+ schema,
+ url,
+ options: {
+ headers: {
+ Authorization: `OreApi session=${BaseOreService.sessionToken}`,
+ },
+ },
+ }
+
+ try {
+ return await this._requestJson(requestOptions)
+ } catch (error) {
+ if (error instanceof InvalidResponse) {
+ const { response: _response } = error
+ const { statusCode } = _response
+ if (statusCode === 401) {
+ await this._refreshSessionToken()
+ requestOptions.options.headers.Authorization = `OreApi session=${BaseOreService.sessionToken}`
+ return this._requestJson(requestOptions)
+ }
+ }
+ throw error
+ }
+ }
+}
+
+BaseOreService.sessionToken = null
+
+export { description, BaseOreService }
diff --git a/services/ore/ore-category.service.js b/services/ore/ore-category.service.js
new file mode 100644
index 0000000000000..1fabb6c5457e3
--- /dev/null
+++ b/services/ore/ore-category.service.js
@@ -0,0 +1,46 @@
+import { pathParams } from '../index.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreCategory extends BaseOreService {
+ static category = 'other'
+
+ static route = {
+ base: 'ore/category',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/category/{pluginId}': {
+ get: {
+ summary: 'Ore Category',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'category',
+ color: 'blue',
+ }
+
+ static render({ category }) {
+ return {
+ message: category,
+ }
+ }
+
+ transform({ data }) {
+ const category = data.category.replace(/_/g, ' ')
+ return { category }
+ }
+
+ async handle({ pluginId }) {
+ const data = await this.fetch({ pluginId })
+ const { category } = this.transform({ data })
+ return this.constructor.render({ category })
+ }
+}
diff --git a/services/ore/ore-category.spec.js b/services/ore/ore-category.spec.js
new file mode 100644
index 0000000000000..793d38ea346f7
--- /dev/null
+++ b/services/ore/ore-category.spec.js
@@ -0,0 +1,19 @@
+import { test, forCases, given } from 'sazerac'
+import OreCategory from './ore-category.service.js'
+
+describe('OreCategory', function () {
+ test(OreCategory.prototype.transform, () => {
+ forCases([
+ given({ data: { category: 'admin_tools' } }),
+ given({ data: { category: 'admin tools' } }),
+ ]).expect({
+ category: 'admin tools',
+ })
+ })
+
+ test(OreCategory.render, () => {
+ given({ category: 'admin tools' }).expect({
+ message: 'admin tools',
+ })
+ })
+})
diff --git a/services/ore/ore-category.tester.js b/services/ore/ore-category.tester.js
new file mode 100644
index 0000000000000..efb62c1324b73
--- /dev/null
+++ b/services/ore/ore-category.tester.js
@@ -0,0 +1,31 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+// Taken from https://ore.spongepowered.org/api#/Projects/showProject
+const CATEGORY_ENUM = [
+ 'admin_tools',
+ 'chat',
+ 'dev_tools',
+ 'economy',
+ 'gameplay',
+ 'games',
+ 'protection',
+ 'role_playing',
+ 'world_management',
+ 'misc',
+]
+
+const isInCategoryEnum = Joi.string()
+ .valid(...CATEGORY_ENUM.map(cat => cat.replace(/_/g, ' ')))
+ .required()
+
+t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
+ label: 'category',
+ message: isInCategoryEnum,
+})
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'category',
+ message: 'not found',
+})
diff --git a/services/ore/ore-downloads.service.js b/services/ore/ore-downloads.service.js
new file mode 100644
index 0000000000000..15f28b4409455
--- /dev/null
+++ b/services/ore/ore-downloads.service.js
@@ -0,0 +1,32 @@
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreDownloads extends BaseOreService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'ore/dt',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/dt/{pluginId}': {
+ get: {
+ summary: 'Ore Downloads',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ pluginId }) {
+ const { stats } = await this.fetch({ pluginId })
+ return renderDownloadsBadge({ downloads: stats.downloads })
+ }
+}
diff --git a/services/ore/ore-downloads.tester.js b/services/ore/ore-downloads.tester.js
new file mode 100644
index 0000000000000..dd2ba54bcfe95
--- /dev/null
+++ b/services/ore/ore-downloads.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
diff --git a/services/ore/ore-license.service.js b/services/ore/ore-license.service.js
new file mode 100644
index 0000000000000..07f870e646f2f
--- /dev/null
+++ b/services/ore/ore-license.service.js
@@ -0,0 +1,50 @@
+import { pathParams } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreLicense extends BaseOreService {
+ static category = 'license'
+
+ static route = {
+ base: 'ore/l',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/l/{pluginId}': {
+ get: {
+ summary: 'Ore License',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'license',
+ }
+
+ static render({ license }) {
+ return renderLicenseBadge({ license })
+ }
+
+ transform({ data }) {
+ const {
+ settings: {
+ license: { name, url },
+ },
+ } = data
+ /* license: { name: '', url: 'https://donationstore.net/legal/eula' }
+ encountered in the wild */
+ return { license: name || (url ? 'custom' : null) || undefined }
+ }
+
+ async handle({ pluginId }) {
+ const data = await this.fetch({ pluginId })
+ const { license } = this.transform({ data })
+ return this.constructor.render({ license })
+ }
+}
diff --git a/services/ore/ore-license.spec.js b/services/ore/ore-license.spec.js
new file mode 100644
index 0000000000000..5e5e922068d75
--- /dev/null
+++ b/services/ore/ore-license.spec.js
@@ -0,0 +1,28 @@
+import { test, given } from 'sazerac'
+import OreLicense from './ore-license.service.js'
+
+describe('OreLicense', function () {
+ test(OreLicense.prototype.transform, () => {
+ given({
+ data: {
+ settings: { license: { name: 'MIT', url: 'http://sample.url' } },
+ },
+ }).expect({ license: 'MIT' })
+ })
+
+ test(OreLicense.prototype.transform, () => {
+ given({
+ data: {
+ settings: { license: { name: null, url: 'http://sample.url' } },
+ },
+ }).expect({ license: 'custom' })
+ })
+
+ test(OreLicense.prototype.transform, () => {
+ given({
+ data: {
+ settings: { license: { name: null, url: null } },
+ },
+ }).expect({ license: undefined })
+ })
+})
diff --git a/services/ore/ore-license.tester.js b/services/ore/ore-license.tester.js
new file mode 100644
index 0000000000000..dd2c441d594bf
--- /dev/null
+++ b/services/ore/ore-license.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
+ label: 'license',
+ message: 'MIT licence',
+})
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'license',
+ message: 'not found',
+})
diff --git a/services/ore/ore-sponge-versions.service.js b/services/ore/ore-sponge-versions.service.js
new file mode 100644
index 0000000000000..0a6c891904ce4
--- /dev/null
+++ b/services/ore/ore-sponge-versions.service.js
@@ -0,0 +1,53 @@
+import { pathParams } from '../index.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreSpongeVersions extends BaseOreService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'ore/sponge-versions',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/sponge-versions/{pluginId}': {
+ get: {
+ summary: 'Compatible versions (plugins on Ore)',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'sponge',
+ }
+
+ static render({ versions }) {
+ if (versions.length === 0) {
+ return { message: 'none', color: 'inactive' }
+ }
+ return { message: versions.join(' | '), color: 'blue' }
+ }
+
+ transform({ data }) {
+ const { promoted_versions: promotedVersions } = data
+ return {
+ versions: promotedVersions
+ .reduce((acc, { tags }) => acc.concat(tags), [])
+ .filter(({ name }) => name.toLowerCase() === 'sponge')
+ .map(({ display_data: displayData }) => displayData)
+ // display_data is not mandatory in the schema, filter null values
+ .filter(x => !!x),
+ }
+ }
+
+ async handle({ pluginId }) {
+ const data = await this.fetch({ pluginId })
+ const { versions } = this.transform({ data })
+ return this.constructor.render({ versions })
+ }
+}
diff --git a/services/ore/ore-sponge-versions.spec.js b/services/ore/ore-sponge-versions.spec.js
new file mode 100644
index 0000000000000..9489c95b2aa5b
--- /dev/null
+++ b/services/ore/ore-sponge-versions.spec.js
@@ -0,0 +1,58 @@
+import { test, forCases, given } from 'sazerac'
+import OreSpongeVersions from './ore-sponge-versions.service.js'
+
+const PROMOTED_VERSIONS = [
+ {
+ version: 'dummy',
+ tags: [{ name: 'sponge', display_data: null }],
+ },
+ {
+ version: 'dummy',
+ tags: [{ name: 'sponge', display_data: '1.23' }],
+ },
+ {
+ version: 'dummy',
+ tags: [{ name: 'sponge', display_data: '4.56' }],
+ },
+]
+
+describe('OreSpongeVersions', function () {
+ test(OreSpongeVersions.prototype.transform, () => {
+ forCases(
+ [given({ data: { promoted_versions: PROMOTED_VERSIONS } })],
+ [
+ given({
+ data: {
+ promoted_versions: [
+ {
+ version: 'dummy',
+ tags: [{ name: 'dummy', display_data: 'dummy' }],
+ },
+ ...PROMOTED_VERSIONS,
+ ],
+ },
+ }),
+ ],
+ ).expect({ versions: ['1.23', '4.56'] })
+ })
+
+ test(OreSpongeVersions.render, () => {
+ given({
+ versions: [],
+ }).expect({ message: 'none', color: 'inactive' })
+ })
+
+ test(OreSpongeVersions.render, () => {
+ given({ versions: ['1.23', '4.56'] }).expect({
+ message: '1.23 | 4.56',
+ color: 'blue',
+ })
+ })
+
+ test(OreSpongeVersions.render, () => {
+ given({ versions: ['1.23'] }).expect({
+ message: '1.23',
+ color: 'blue',
+ })
+ })
+})
diff --git a/services/ore/ore-sponge-versions.tester.js b/services/ore/ore-sponge-versions.tester.js
new file mode 100644
index 0000000000000..0d1d6e0570e91
--- /dev/null
+++ b/services/ore/ore-sponge-versions.tester.js
@@ -0,0 +1,23 @@
+import joiModule from 'joi'
+import { createServiceTester } from '../tester.js'
+const Joi = joiModule.extend(joi => ({
+ base: joi.array(),
+ coerce: (value, helpers) => ({
+ value: value.split ? value.split(' | ') : value,
+ }),
+ type: 'versionArray',
+}))
+const isDottedVersionAtLeastOne = Joi.string().regex(/\d+(\.\d+)?(\.\d+)?$/)
+export const t = await createServiceTester()
+
+t.create('Nucleus (pluginId nucleus)')
+ .get('/nucleus.json')
+ .expectBadge({
+ label: 'sponge',
+ message: Joi.versionArray().items(isDottedVersionAtLeastOne),
+ })
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'sponge',
+ message: 'not found',
+})
diff --git a/services/ore/ore-stars.service.js b/services/ore/ore-stars.service.js
new file mode 100644
index 0000000000000..276643dd983e7
--- /dev/null
+++ b/services/ore/ore-stars.service.js
@@ -0,0 +1,42 @@
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreStars extends BaseOreService {
+ static category = 'rating'
+
+ static route = {
+ base: 'ore/stars',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/stars/{pluginId}': {
+ get: {
+ summary: 'Ore Stars',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'stars',
+ color: 'blue',
+ }
+
+ static render({ stars }) {
+ return {
+ message: metric(stars),
+ }
+ }
+
+ async handle({ pluginId }) {
+ const { stats } = await this.fetch({ pluginId })
+ const { stars } = stats
+ return this.constructor.render({ stars })
+ }
+}
diff --git a/services/ore/ore-stars.tester.js b/services/ore/ore-stars.tester.js
new file mode 100644
index 0000000000000..c80e67d43d57b
--- /dev/null
+++ b/services/ore/ore-stars.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
+ label: 'stars',
+ message: isMetric,
+})
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'stars',
+ message: 'not found',
+})
diff --git a/services/ore/ore-version.service.js b/services/ore/ore-version.service.js
new file mode 100644
index 0000000000000..2f1f72b417a00
--- /dev/null
+++ b/services/ore/ore-version.service.js
@@ -0,0 +1,50 @@
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseOreService, description } from './ore-base.js'
+
+export default class OreVersion extends BaseOreService {
+ static category = 'version'
+
+ static route = {
+ base: 'ore/v',
+ pattern: ':pluginId',
+ }
+
+ static openApi = {
+ '/ore/v/{pluginId}': {
+ get: {
+ summary: 'Ore Version',
+ description,
+ parameters: pathParams({
+ name: 'pluginId',
+ example: 'nucleus',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'version',
+ }
+
+ static render({ version }) {
+ if (!version) {
+ return { message: 'none', color: 'inactive' }
+ }
+ return renderVersionBadge({ version })
+ }
+
+ transform({ data }) {
+ const { promoted_versions: promotedVersions } = data
+ return {
+ version:
+ promotedVersions.length === 0 ? undefined : promotedVersions[0].version,
+ }
+ }
+
+ async handle({ pluginId }) {
+ const data = await this.fetch({ pluginId })
+ const { version } = this.transform({ data })
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/ore/ore-version.spec.js b/services/ore/ore-version.spec.js
new file mode 100644
index 0000000000000..0d50f35acecd5
--- /dev/null
+++ b/services/ore/ore-version.spec.js
@@ -0,0 +1,23 @@
+import { test, given } from 'sazerac'
+import OreVersion from './ore-version.service.js'
+
+describe('OreVersion', function () {
+ test(OreVersion.prototype.transform, () => {
+ given({
+ data: { promoted_versions: [{ version: '2.3' }, { version: '4.5' }] },
+ }).expect({ version: '2.3' })
+ })
+
+ test(OreVersion.prototype.transform, () => {
+ given({
+ data: { promoted_versions: [] },
+ }).expect({ version: undefined })
+ })
+
+ test(OreVersion.render, () => {
+ given({ version: undefined }).expect({
+ message: 'none',
+ color: 'inactive',
+ })
+ })
+})
diff --git a/services/ore/ore-version.tester.js b/services/ore/ore-version.tester.js
new file mode 100644
index 0000000000000..4583564e873ac
--- /dev/null
+++ b/services/ore/ore-version.tester.js
@@ -0,0 +1,13 @@
+import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
+ label: 'version',
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
+})
+
+t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({
+ label: 'version',
+ message: 'not found',
+})
diff --git a/services/ossf-scorecard/ossf-scorecard.service.js b/services/ossf-scorecard/ossf-scorecard.service.js
new file mode 100644
index 0000000000000..1ee57a04017b9
--- /dev/null
+++ b/services/ossf-scorecard/ossf-scorecard.service.js
@@ -0,0 +1,65 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+
+const schema = Joi.object({
+ score: Joi.number().min(0).required(),
+}).required()
+
+const ossfScorecardColorScale = colorScale(
+ [2, 5, 8, 10],
+ ['red', 'yellow', 'yellowgreen', 'green', 'brightgreen'],
+)
+
+export default class OSSFScorecard extends BaseJsonService {
+ static category = 'analysis'
+
+ static route = { base: 'ossf-scorecard', pattern: ':host/:orgName/:repoName' }
+
+ static openApi = {
+ '/ossf-scorecard/{host}/{orgName}/{repoName}': {
+ get: {
+ summary: 'OSSF-Scorecard Score',
+ parameters: pathParams(
+ {
+ name: 'host',
+ example: 'github.com',
+ },
+ {
+ name: 'orgName',
+ example: 'rohankh532',
+ },
+ {
+ name: 'repoName',
+ example: 'org-workflow-add',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'score' }
+
+ static render({ score }) {
+ return {
+ message: score,
+ color: ossfScorecardColorScale(score),
+ }
+ }
+
+ async fetch({ host, orgName, repoName }) {
+ return this._requestJson({
+ schema,
+ url: `https://api.securityscorecards.dev/projects/${host}/${orgName}/${repoName}`,
+ httpErrors: {
+ 404: 'invalid repo path',
+ },
+ })
+ }
+
+ async handle({ host, orgName, repoName }) {
+ const { score } = await this.fetch({ host, orgName, repoName })
+
+ return this.constructor.render({ score })
+ }
+}
diff --git a/services/ossf-scorecard/ossf-scorecard.tester.js b/services/ossf-scorecard/ossf-scorecard.tester.js
new file mode 100644
index 0000000000000..ba107664283ec
--- /dev/null
+++ b/services/ossf-scorecard/ossf-scorecard.tester.js
@@ -0,0 +1,19 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('score valid')
+ .get('/github.com/rohankh532/org-workflow-add.json')
+ .expectBadge({
+ label: 'score',
+ message: Joi.number().min(0),
+ color: Joi.equal('red', 'yellow', 'yellowgreen', 'green', 'brightgreen'),
+ })
+
+t.create('score ivalid')
+ .get('/github.com/invalid-user/invalid-repo.json')
+ .expectBadge({
+ label: 'score',
+ message: 'invalid repo path',
+ color: 'red',
+ })
diff --git a/services/osslifecycle/osslifecycle-redirector.service.js b/services/osslifecycle/osslifecycle-redirector.service.js
new file mode 100644
index 0000000000000..abdefb0a91536
--- /dev/null
+++ b/services/osslifecycle/osslifecycle-redirector.service.js
@@ -0,0 +1,20 @@
+import { redirector } from '../index.js'
+
+const commonProps = {
+ category: 'other',
+ dateAdded: new Date('2024-09-02'),
+}
+
+export default [
+ redirector({
+ route: {
+ base: 'osslifecycle',
+ pattern: ':user/:repo/:branch*',
+ },
+ transformPath: () => '/osslifecycle',
+ transformQueryParams: ({ user, repo, branch }) => ({
+ file_url: `https://raw.githubusercontent.com/${user}/${repo}/${branch || 'HEAD'}/OSSMETADATA`,
+ }),
+ ...commonProps,
+ }),
+]
diff --git a/services/osslifecycle/osslifecycle-redirector.tester.js b/services/osslifecycle/osslifecycle-redirector.tester.js
new file mode 100644
index 0000000000000..e098229b436d5
--- /dev/null
+++ b/services/osslifecycle/osslifecycle-redirector.tester.js
@@ -0,0 +1,26 @@
+import queryString from 'querystring'
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'osslifecycleRedirect',
+ title: 'OSSLifecycleRedirect',
+ pathPrefix: '/osslifecycle',
+})
+
+t.create('oss lifecycle redirect')
+ .get('/netflix/osstracker.svg')
+ .expectRedirect(
+ `/osslifecycle.svg?${queryString.stringify({
+ file_url:
+ 'https://raw.githubusercontent.com/netflix/osstracker/HEAD/OSSMETADATA',
+ })}`,
+ )
+
+t.create('oss lifecycle redirect (branch)')
+ .get('/netflix/osstracker/documentation.svg')
+ .expectRedirect(
+ `/osslifecycle.svg?${queryString.stringify({
+ file_url:
+ 'https://raw.githubusercontent.com/netflix/osstracker/documentation/OSSMETADATA',
+ })}`,
+ )
diff --git a/services/osslifecycle/osslifecycle.service.js b/services/osslifecycle/osslifecycle.service.js
index 6869547e4d05e..ffb547916df60 100644
--- a/services/osslifecycle/osslifecycle.service.js
+++ b/services/osslifecycle/osslifecycle.service.js
@@ -1,58 +1,48 @@
-'use strict'
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseService, InvalidResponse, queryParam } from '../index.js'
-const { BaseService, InvalidResponse } = require('..')
-
-const documentation = `
-
- OSS Lifecycle is an initiative started by Netflix to classify open-source projects into lifecycles
- and clearly identify which projects are active and which ones are retired. To enable this badge,
- simply create an OSSMETADATA tagging file at the root of your GitHub repository containing a
- single line similar to the following: osslifecycle=active. Other suggested values are
- osslifecycle=maintenance and osslifecycle=archived. A working example
- can be viewed on the OSS Tracker repository .
-
+const description = `
+OSS Lifecycle is an initiative started by Netflix to classify open-source projects into lifecycles
+and clearly identify which projects are active and which ones are retired. To enable this badge,
+simply create an OSSMETADATA tagging file at the root of your repository containing a
+single line similar to the following: \`osslifecycle=active\`. Other suggested values are
+\`osslifecycle=maintenance\` and \`osslifecycle=archived\`. A working example
+can be viewed on the [OSS Tracker repository](https://github.com/Netflix/osstracker).
`
-module.exports = class OssTracker extends BaseService {
- static get category() {
- return 'other'
- }
+const queryParamSchema = Joi.object({
+ file_url: url,
+}).required()
- static get route() {
- return {
- base: 'osslifecycle',
- pattern: ':user/:repo/:branch*',
- }
+export default class OssTracker extends BaseService {
+ static category = 'other'
+
+ static route = {
+ base: '',
+ pattern: 'osslifecycle',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'OSS Lifecycle',
- pattern: ':user/:repo',
- namedParams: { user: 'Teevity', repo: 'ice' },
- staticPreview: this.render({ status: 'active' }),
- keywords: ['Netflix'],
- documentation,
- },
- {
- title: 'OSS Lifecycle (branch)',
- pattern: ':user/:repo/:branch',
- namedParams: {
- user: 'Netflix',
- repo: 'osstracker',
- branch: 'documentation',
- },
- staticPreview: this.render({ status: 'active' }),
- keywords: ['Netflix'],
- documentation,
+ static openApi = {
+ '/osslifecycle': {
+ get: {
+ summary: 'OSS Lifecycle',
+ description,
+ parameters: [
+ queryParam({
+ name: 'file_url',
+ example:
+ 'https://raw.githubusercontent.com/Netflix/aws-autoscaling/master/OSSMETADATA',
+ required: true,
+ description: 'URL for the `OSSMETADATA` file',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'oss lifecycle' }
- }
+ static defaultBadgeData = { label: 'oss lifecycle' }
/**
* Return color for active, maintenance and archived statuses, which were the three
@@ -83,17 +73,15 @@ module.exports = class OssTracker extends BaseService {
}
}
- async fetch({ user, repo, branch }) {
+ async fetch({ fileUrl }) {
return this._request({
- url: `https://raw.githubusercontent.com/${user}/${repo}/${branch}/OSSMETADATA`,
+ url: fileUrl,
})
}
- async handle({ user, repo, branch }) {
+ async handle(pathParams, { file_url: fileUrl = '' }) {
const { buffer } = await this.fetch({
- user,
- repo,
- branch: branch || 'master',
+ fileUrl,
})
try {
const status = buffer.match(/osslifecycle=([a-z]+)/im)[1]
diff --git a/services/osslifecycle/osslifecycle.tester.js b/services/osslifecycle/osslifecycle.tester.js
index df50a2dd993cc..2599d936bdbf8 100644
--- a/services/osslifecycle/osslifecycle.tester.js
+++ b/services/osslifecycle/osslifecycle.tester.js
@@ -1,14 +1,14 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'osslifecycle',
title: 'OSS Lifecycle',
-}))
+})
t.create('osslifecycle active status')
- .get('/netflix/sureal.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/Netflix/sureal/HEAD/OSSMETADATA',
+ )
.expectBadge({
label: 'oss lifecycle',
message: 'active',
@@ -16,7 +16,9 @@ t.create('osslifecycle active status')
})
t.create('osslifecycle maintenance status')
- .get('/Teevity/ice.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/Teevity/ice/HEAD/OSSMETADATA',
+ )
.expectBadge({
label: 'oss lifecycle',
message: 'maintenance',
@@ -24,7 +26,9 @@ t.create('osslifecycle maintenance status')
})
t.create('osslifecycle archived status')
- .get('/Netflix/rx-aws-java-sdk.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/Netflix/rx-aws-java-sdk/HEAD/OSSMETADATA',
+ )
.expectBadge({
label: 'oss lifecycle',
message: 'archived',
@@ -32,30 +36,27 @@ t.create('osslifecycle archived status')
})
t.create('osslifecycle other status')
- .get('/Netflix/metacat.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/Netflix/metacat/HEAD/OSSMETADATA',
+ )
.expectBadge({
label: 'oss lifecycle',
message: 'private',
color: 'lightgrey',
})
-t.create('osslifecycle status (branch)')
- .get('/Netflix/osstracker/documentation.json')
- .expectBadge({
- label: 'oss lifecycle',
- message: 'active',
- })
-
t.create('oss metadata in unexpected format')
- .get('/some-user/some-project.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/some-user/some-project/HEAD/OSSMETADATA',
+ )
.intercept(
nock =>
nock('https://raw.githubusercontent.com')
- .get('/some-user/some-project/master/OSSMETADATA')
- .reply(200, `wrongkey=active`),
+ .get('/some-user/some-project/HEAD/OSSMETADATA')
+ .reply(200, 'wrongkey=active'),
{
'Content-Type': 'text/plain;charset=UTF-8',
- }
+ },
)
.expectBadge({
label: 'oss lifecycle',
@@ -63,7 +64,9 @@ t.create('oss metadata in unexpected format')
})
t.create('oss metadata not found')
- .get('/PyvesB/empty-repo.json')
+ .get(
+ '.json?file_url=https://raw.githubusercontent.com/PyvesB/empty-repo/HEAD/OSSMETADATA',
+ )
.expectBadge({
label: 'oss lifecycle',
message: 'not found',
diff --git a/services/package-json-helpers.js b/services/package-json-helpers.js
index 2906132870f0a..1c913e1362e56 100644
--- a/services/package-json-helpers.js
+++ b/services/package-json-helpers.js
@@ -1,54 +1,78 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to package.json
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
-const { InvalidParameter } = require('.')
+import Joi from 'joi'
+import { InvalidParameter } from './index.js'
+/**
+ * Joi schema for validating dependency map.
+ *
+ * @type {Joi}
+ */
const isDependencyMap = Joi.object()
.pattern(
/./,
// This accepts a semver range, a URL, and many other possible values.
- Joi.string()
- .min(1)
- .required()
+ Joi.string().min(1).required(),
)
.default({})
+/**
+ * Joi schema for validating package json object.
+ * Checks if the object has all the dependency types and the dependency types are valid.
+ *
+ * @type {Joi}
+ */
const isPackageJsonWithDependencies = Joi.object({
dependencies: isDependencyMap,
devDependencies: isDependencyMap,
peerDependencies: isDependencyMap,
+ optionalDependencies: isDependencyMap,
}).required()
+/**
+ * Determines the dependency version based on the dependency type.
+ *
+ * @param {object} attrs - Refer to individual attributes
+ * @param {string} attrs.kind - Wanted dependency type, defaults to prod
+ * @param {string} attrs.wantedDependency - Name of the wanted dependency
+ * @param {object} attrs.dependencies - Map of dependencies
+ * @param {object} attrs.devDependencies - Map of dev dependencies
+ * @param {object} attrs.peerDependencies - Map of peer dependencies
+ * @param {object} attrs.optionalDependencies - Map of optional dependencies
+ * @throws {string} - Error message if unknown dependency type provided
+ * @throws {InvalidParameter} - Error if wanted dependency is not present
+ * @returns {string} Semver range of the wanted dependency (eg. ~2.1.6 or >=3.0.0 or <4.0.0)
+ */
function getDependencyVersion({
kind = 'prod',
wantedDependency,
dependencies,
devDependencies,
peerDependencies,
+ optionalDependencies,
}) {
- let dependenciesOfKind
- if (kind === 'peer') {
- dependenciesOfKind = peerDependencies
- } else if (kind === 'dev') {
- dependenciesOfKind = devDependencies
- } else if (kind === 'prod') {
- dependenciesOfKind = dependencies
- } else {
- throw Error(`Not very kind: ${kind}`)
+ const dependencyMaps = {
+ peer: peerDependencies,
+ optional: optionalDependencies,
+ dev: devDependencies,
+ prod: dependencies,
}
- const range = dependenciesOfKind[wantedDependency]
+ if (!(kind in dependencyMaps)) {
+ throw Error(`Not very kind: ${kind}`)
+ }
+ const range = dependencyMaps[kind][wantedDependency]
if (range === undefined) {
throw new InvalidParameter({
prettyMessage: `${kind} dependency not found`,
})
}
- return { range }
+ return range
}
-module.exports = {
- isDependencyMap,
- isPackageJsonWithDependencies,
- getDependencyVersion,
-}
+export { isDependencyMap, isPackageJsonWithDependencies, getDependencyVersion }
diff --git a/services/package-json-helpers.spec.js b/services/package-json-helpers.spec.js
index 628d02197c998..6180eff24c989 100644
--- a/services/package-json-helpers.spec.js
+++ b/services/package-json-helpers.spec.js
@@ -1,18 +1,15 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { getDependencyVersion } from './package-json-helpers.js'
-const { test, given } = require('sazerac')
-const { getDependencyVersion } = require('./package-json-helpers')
-
-describe('Contributor count helpers', function() {
+describe('Package json helpers', function () {
test(getDependencyVersion, () => {
given({
wantedDependency: 'left-pad',
dependencies: { 'left-pad': '~1.2.3' },
devDependencies: {},
peerDependencies: {},
- }).expect({
- range: '~1.2.3',
- })
+ }).expect('~1.2.3')
+
given({
kind: 'dev',
wantedDependency: 'left-pad',
@@ -20,24 +17,23 @@ describe('Contributor count helpers', function() {
devDependencies: {},
peerDependencies: {},
}).expectError('Invalid Parameter')
+
given({
kind: 'dev',
wantedDependency: 'left-pad',
dependencies: {},
devDependencies: { 'left-pad': '~1.2.3' },
peerDependencies: {},
- }).expect({
- range: '~1.2.3',
- })
+ }).expect('~1.2.3')
+
given({
kind: 'peer',
wantedDependency: 'left-pad',
dependencies: {},
devDependencies: {},
peerDependencies: { 'left-pad': '~1.2.3' },
- }).expect({
- range: '~1.2.3',
- })
+ }).expect('~1.2.3')
+
given({
kind: 'notreal',
wantedDependency: 'left-pad',
diff --git a/services/packagecontrol/packagecontrol.service.js b/services/packagecontrol/packagecontrol.service.js
index c02f0caa4b395..442d266dbf34d 100644
--- a/services/packagecontrol/packagecontrol.service.js
+++ b/services/packagecontrol/packagecontrol.service.js
@@ -1,12 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const keywords = ['sublime', 'sublimetext', 'packagecontrol']
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
installs: Joi.object({
@@ -15,117 +10,99 @@ const schema = Joi.object({
data: Joi.array()
.items(
Joi.object({
- totals: Joi.array()
- .items(nonNegativeInteger)
- .required(),
- }).required()
+ totals: Joi.array().items(nonNegativeInteger).required(),
+ }).required(),
)
.required(),
}).required(),
}).required(),
})
-function DownloadsForInterval(interval) {
- const { base, messageSuffix, transform, name } = {
- day: {
- base: 'packagecontrol/dd',
- messageSuffix: '/day',
- transform: resp => {
- const platforms = resp.installs.daily.data
- let downloads = 0
- platforms.forEach(platform => {
- // use the downloads from yesterday
- downloads += platform.totals[1]
- })
- return downloads
- },
- name: 'PackageControlDownloadsDay',
- },
- week: {
- base: 'packagecontrol/dw',
- messageSuffix: '/week',
- transform: resp => {
- const platforms = resp.installs.daily.data
- let downloads = 0
- platforms.forEach(platform => {
- // total for the first 7 days
- for (let i = 0; i < 7; i++) {
- downloads += platform.totals[i]
- }
- })
- return downloads
- },
- name: 'PackageControlDownloadsWeek',
+const intervalMap = {
+ dd: {
+ label: 'day',
+ transform: resp => {
+ const platforms = resp.installs.daily.data
+ let downloads = 0
+ platforms.forEach(platform => {
+ // use the downloads from yesterday
+ downloads += platform.totals[1]
+ })
+ return downloads
},
- month: {
- base: 'packagecontrol/dm',
- messageSuffix: '/month',
- transform: resp => {
- const platforms = resp.installs.daily.data
- let downloads = 0
- platforms.forEach(platform => {
- // total for the first 30 days
- for (let i = 0; i < 30; i++) {
- downloads += platform.totals[i]
- }
- })
- return downloads
- },
- name: 'PackageControlDownloadsMonth',
+ },
+ dw: {
+ label: 'week',
+ transform: resp => {
+ const platforms = resp.installs.daily.data
+ let downloads = 0
+ platforms.forEach(platform => {
+ // total for the first 7 days
+ for (let i = 0; i < 7; i++) {
+ downloads += platform.totals[i]
+ }
+ })
+ return downloads
},
- total: {
- base: 'packagecontrol/dt',
- messageSuffix: '',
- transform: resp => resp.installs.total,
- name: 'PackageControlDownloadsTotal',
+ },
+ dm: {
+ label: 'month',
+ transform: resp => {
+ const platforms = resp.installs.daily.data
+ let downloads = 0
+ platforms.forEach(platform => {
+ // total for the first 30 days
+ for (let i = 0; i < 30; i++) {
+ downloads += platform.totals[i]
+ }
+ })
+ return downloads
},
- }[interval]
-
- return class PackageControlDownloads extends BaseJsonService {
- static get name() {
- return name
- }
-
- static get category() {
- return 'downloads'
- }
+ },
+ dt: {
+ transform: resp => resp.installs.total,
+ },
+}
- static get route() {
- return { base, pattern: ':packageName' }
- }
+export default class PackageControlDownloads extends BaseJsonService {
+ static category = 'downloads'
- static get examples() {
- return [
- {
- title: 'Package Control',
- namedParams: { packageName: 'GitGutter' },
- staticPreview: this.render({ downloads: 12000 }),
- keywords,
- },
- ]
- }
+ static route = {
+ base: 'packagecontrol',
+ pattern: ':interval(dd|dw|dm|dt)/:packageName',
+ }
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static openApi = {
+ '/packagecontrol/{interval}/{packageName}': {
+ get: {
+ summary: 'Package Control Downloads',
+ description:
+ 'Package Control is a package registry for Sublime Text packages',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dt',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Weekly, Monthly, or Total downloads',
+ },
+ { name: 'packageName', example: 'GitGutter' },
+ ),
+ },
+ },
+ }
- static render({ downloads }) {
- return {
- message: `${metric(downloads)}${messageSuffix}`,
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
- async fetch({ packageName }) {
- const url = `https://packagecontrol.io/packages/${packageName}.json`
- return this._requestJson({ schema, url })
- }
+ async fetch({ packageName }) {
+ const url = `https://packagecontrol.io/packages/${packageName}.json`
+ return this._requestJson({ schema, url })
+ }
- async handle({ packageName }) {
- const data = await this.fetch({ packageName })
- return this.constructor.render({ downloads: transform(data) })
- }
+ async handle({ interval, packageName }) {
+ const data = await this.fetch({ packageName })
+ return renderDownloadsBadge({
+ downloads: intervalMap[interval].transform(data),
+ interval: intervalMap[interval].label,
+ })
}
}
-
-module.exports = ['day', 'week', 'month', 'total'].map(DownloadsForInterval)
diff --git a/services/packagecontrol/packagecontrol.tester.js b/services/packagecontrol/packagecontrol.tester.js
index 4b29a5ad9ee9c..db61cf1251414 100644
--- a/services/packagecontrol/packagecontrol.tester.js
+++ b/services/packagecontrol/packagecontrol.tester.js
@@ -1,44 +1,32 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'packagecontrol',
title: 'Package Control',
-}))
+})
-t.create('monthly downloads')
- .get('/dm/GitGutter.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('monthly downloads').get('/dm/GitGutter.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
-t.create('weekly downloads')
- .get('/dw/GitGutter.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('weekly downloads').get('/dw/GitGutter.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
-t.create('daily downloads')
- .get('/dd/GitGutter.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('daily downloads').get('/dd/GitGutter.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
-t.create('total downloads')
- .get('/dt/GitGutter.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('total downloads').get('/dt/GitGutter.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
-t.create('package not found')
- .get('/dt/does-not-exist.json')
- .expectBadge({
- label: 'downloads',
- message: 'not found',
- })
+t.create('package not found').get('/dt/does-not-exist.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
diff --git a/services/packagist/packagist-base.js b/services/packagist/packagist-base.js
index 7bcdc2dfcb5d6..e3e18fb5519e5 100644
--- a/services/packagist/packagist-base.js
+++ b/services/packagist/packagist-base.js
@@ -1,40 +1,68 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const packageSchema = Joi.object()
- .pattern(
- /^/,
- Joi.object({
- version: Joi.string(),
- require: Joi.object({
- php: Joi.string(),
- }),
- }).required()
- )
- .required()
+import Joi from 'joi'
+import { BaseJsonService, NotFound } from '../index.js'
+import { isStable, latest } from '../php-version.js'
+
+const packageSchema = Joi.array().items(
+ Joi.object({
+ version: Joi.string().required(),
+ require: Joi.alternatives(
+ Joi.object().pattern(Joi.string(), Joi.string()).required(),
+ Joi.string().valid('__unset'),
+ ),
+ }),
+)
const allVersionsSchema = Joi.object({
- packages: Joi.object()
- .pattern(/^/, packageSchema)
- .required(),
+ packages: Joi.object().pattern(/^/, packageSchema).required(),
}).required()
-const keywords = ['PHP']
class BasePackagistService extends BaseJsonService {
/**
* Default fetch method.
*
- * This method utilize composer metadata API which
+ * This method utilizes composer metadata API which
+ * "... is the preferred way to access the data as it is always up to date,
+ * and dumped to static files so it is very efficient on our end." (comment from official documentation).
+ * For more information please refer to https://packagist.org/apidoc#get-package-data.
+ *
+ * @param {object} attrs Refer to individual attrs
+ * @param {string} attrs.user package user
+ * @param {string} attrs.repo package repository
+ * @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
+ * @param {string} attrs.server URL for the packagist registry server (Optional)
+ * @returns {object} Parsed response
+ */
+ async fetch({ user, repo, schema, server = 'https://repo.packagist.org' }) {
+ const url = `${server}/p2/${user.toLowerCase()}/${repo.toLowerCase()}.json`
+
+ return this._requestJson({
+ schema,
+ url,
+ })
+ }
+
+ /**
+ * Fetch dev releases method.
+ *
+ * This method utilizes composer metadata API which
* "... is the preferred way to access the data as it is always up to date,
* and dumped to static files so it is very efficient on our end." (comment from official documentation).
* For more information please refer to https://packagist.org/apidoc#get-package-data.
*
+ * @param {object} attrs Refer to individual attrs
+ * @param {string} attrs.user package user
+ * @param {string} attrs.repo package repository
+ * @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
+ * @param {string} attrs.server URL for the packagist registry server (Optional)
* @returns {object} Parsed response
*/
- async fetch({ user, repo, schema, server = 'https://packagist.org' }) {
- const url = `${server}/p/${user.toLowerCase()}/${repo.toLowerCase()}.json`
+ async fetchDev({
+ user,
+ repo,
+ schema,
+ server = 'https://repo.packagist.org',
+ }) {
+ const url = `${server}/p2/${user.toLowerCase()}/${repo.toLowerCase()}~dev.json`
return this._requestJson({
schema,
@@ -50,6 +78,11 @@ class BasePackagistService extends BaseJsonService {
* so data fetch from this method might be outdated.
* For more information please refer to https://packagist.org/apidoc#get-package-data.
*
+ * @param {object} attrs Refer to individual attrs
+ * @param {string} attrs.user package user
+ * @param {string} attrs.repo package repository
+ * @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
+ * @param {string} attrs.server URL for the packagist registry server (Optional)
* @returns {object} Parsed response
*/
async fetchByJsonAPI({
@@ -69,26 +102,91 @@ class BasePackagistService extends BaseJsonService {
getPackageName(user, repo) {
return `${user.toLowerCase()}/${repo.toLowerCase()}`
}
+
+ /**
+ * Extract the array of minified versions of the given packageName,
+ * expand them back to their original format then return.
+ *
+ * @param {object} json The response of Packagist v2 API.
+ * @param {string} packageName The package name.
+ *
+ * @returns {object[]} An array of version metadata object.
+ *
+ * @see https://github.com/composer/metadata-minifier/blob/c549d23829536f0d0e984aaabbf02af91f443207/src/MetadataMinifier.php#L16-L46
+ */
+ static expandPackageVersions(json, packageName) {
+ const versions = json.packages[packageName]
+ const expanded = []
+ let expandedVersion = null
+
+ for (const i in versions) {
+ const versionData = versions[i]
+ if (!expandedVersion) {
+ expandedVersion = { ...versionData }
+ expanded.push(expandedVersion)
+ continue
+ }
+
+ expandedVersion = { ...expandedVersion, ...versionData }
+ for (const key in expandedVersion) {
+ if (expandedVersion[key] === '__unset') {
+ delete expandedVersion[key]
+ }
+ }
+ expanded.push(expandedVersion)
+ }
+
+ return expanded
+ }
+
+ /**
+ * Find the object representation of the latest release.
+ *
+ * @param {object[]} versions An array of object representing a version.
+ * @param {boolean} includePrereleases Includes pre-release semver for the search.
+ *
+ * @returns {object} The object of the latest version.
+ * @throws {NotFound} Thrown if there is no item from the version array.
+ */
+ findLatestRelease(versions, includePrereleases = false) {
+ // Find the latest version string, if not found, throw NotFound.
+ const versionStrings = versions
+ .filter(
+ version =>
+ typeof version.version === 'string' ||
+ version.version instanceof String,
+ )
+ .map(version => version.version)
+ if (versionStrings.length < 1) {
+ throw new NotFound({ prettyMessage: 'no released version found' })
+ }
+
+ let release = latest(versionStrings)
+ if (!includePrereleases) {
+ release = latest(versionStrings.filter(isStable)) || release
+ }
+ return versions.filter(version => version.version === release)[0]
+ }
}
+const description = `
+Packagist is a registry for PHP packages which can be installed with Composer.
+`
+
const customServerDocumentationFragment = `
-
- Note that only network-accessible packagist.org and other self-hosted Packagist instances are supported.
-
- `
+Note that only network-accessible packagist.org and other self-hosted Packagist instances are supported.
+`
const cacheDocumentationFragment = `
-
- Displayed data may be slightly outdated.
- Due to performance reasons, data fetched from packagist JSON API is cached for twelve hours on packagist infrastructure.
- For more information please refer to official packagist documentation .
-
- `
-
-module.exports = {
+Displayed data may be slightly outdated.
+Due to performance reasons, data fetched from packagist JSON API is cached for twelve hours on packagist infrastructure.
+For more information please refer to official packagist documentation .
+`
+
+export {
allVersionsSchema,
- keywords,
BasePackagistService,
+ description,
customServerDocumentationFragment,
cacheDocumentationFragment,
}
diff --git a/services/packagist/packagist-base.spec.js b/services/packagist/packagist-base.spec.js
new file mode 100644
index 0000000000000..ba97113327af2
--- /dev/null
+++ b/services/packagist/packagist-base.spec.js
@@ -0,0 +1,77 @@
+import { strict as assert } from 'assert'
+import { describe, it } from 'mocha'
+import { BasePackagistService } from './packagist-base.js'
+
+// @reference: https://github.com/composer/metadata-minifier/blob/c549d23829536f0d0e984aaabbf02af91f443207/tests/MetadataMinifierTest.php#L36-L40
+const minifiedSample = [
+ {
+ name: 'foo/bar',
+ version: '2.0.0',
+ version_normalized: '2.0.0.0',
+ type: 'library',
+ scripts: {
+ foo: 'bar',
+ },
+ license: ['MIT'],
+ },
+ {
+ version: '1.2.0',
+ version_normalized: '1.2.0.0',
+ license: ['GPL'],
+ homepage: 'https://example.org',
+ scripts: '__unset',
+ },
+ {
+ version: '1.0.0',
+ version_normalized: '1.0.0.0',
+ homepage: '__unset',
+ },
+]
+
+const expandedSample = [
+ {
+ name: 'foo/bar',
+ version: '2.0.0',
+ version_normalized: '2.0.0.0',
+ type: 'library',
+ scripts: {
+ foo: 'bar',
+ },
+ license: ['MIT'],
+ },
+ {
+ name: 'foo/bar',
+ version: '1.2.0',
+ version_normalized: '1.2.0.0',
+ type: 'library',
+ license: ['GPL'],
+ homepage: 'https://example.org',
+ },
+ {
+ name: 'foo/bar',
+ version: '1.0.0',
+ version_normalized: '1.0.0.0',
+ type: 'library',
+ license: ['GPL'],
+ },
+]
+
+describe('BasePackagistService', () => {
+ describe('expandPackageVersions', () => {
+ const expanded = BasePackagistService.expandPackageVersions(
+ {
+ packages: {
+ 'foobar/foobar': minifiedSample,
+ },
+ },
+ 'foobar/foobar',
+ )
+ it('should expand the minified package array to match the expanded sample', () => {
+ assert.deepStrictEqual(
+ expanded,
+ expandedSample,
+ 'The expanded array should match the sample',
+ )
+ })
+ })
+})
diff --git a/services/packagist/packagist-dependency-version.service.js b/services/packagist/packagist-dependency-version.service.js
new file mode 100644
index 0000000000000..7862aad4ee722
--- /dev/null
+++ b/services/packagist/packagist-dependency-version.service.js
@@ -0,0 +1,177 @@
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import {
+ allVersionsSchema,
+ BasePackagistService,
+ customServerDocumentationFragment,
+ description,
+} from './packagist-base.js'
+
+const queryParamSchema = Joi.object({
+ server: optionalUrl,
+ version: Joi.string(),
+}).required()
+
+export default class PackagistDependencyVersion extends BasePackagistService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'packagist/dependency-v',
+ pattern: ':user/:repo/:dependency+',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/packagist/dependency-v/{user}/{repo}/{dependency}': {
+ get: {
+ summary: 'Packagist Dependency Version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'guzzlehttp',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'guzzle',
+ }),
+ pathParam({
+ name: 'dependency',
+ example: 'php',
+ description:
+ '`dependency` can be a PHP package like `twig/twig` or a platform/extension like `php` or `ext-xml`',
+ }),
+ queryParam({
+ name: 'version',
+ example: 'v2.8.0',
+ }),
+ queryParam({
+ name: 'server',
+ description: customServerDocumentationFragment,
+ example: 'https://packagist.org',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'dependency version',
+ color: 'blue',
+ }
+
+ static render({ dependency, dependencyVersion }) {
+ return {
+ label: dependency,
+ message: dependencyVersion,
+ }
+ }
+
+ async getDependencyVersion({
+ json,
+ user,
+ repo,
+ dependency,
+ version = '',
+ server,
+ }) {
+ let packageVersion
+ const versions = BasePackagistService.expandPackageVersions(
+ json,
+ this.getPackageName(user, repo),
+ )
+
+ if (version === '') {
+ packageVersion = this.findLatestRelease(versions)
+ } else {
+ try {
+ packageVersion = await this.findSpecifiedVersion(
+ versions,
+ user,
+ repo,
+ version,
+ server,
+ )
+ } catch (e) {
+ packageVersion = null
+ }
+ }
+
+ if (!packageVersion) {
+ throw new NotFound({ prettyMessage: 'invalid version' })
+ }
+
+ if (!packageVersion.require) {
+ throw new NotFound({ prettyMessage: 'version requirement not found' })
+ }
+
+ // All dependencies' names in the 'require' section from the response should be lowercase,
+ // so that we can compare lowercase name of the dependency given via url by the user.
+ Object.keys(packageVersion.require).forEach(dependency => {
+ packageVersion.require[dependency.toLowerCase()] =
+ packageVersion.require[dependency]
+ })
+
+ const depLowerCase = dependency.toLowerCase()
+
+ if (!packageVersion.require[depLowerCase]) {
+ throw new NotFound({ prettyMessage: 'version requirement not found' })
+ }
+
+ return { dependencyVersion: packageVersion.require[depLowerCase] }
+ }
+
+ async handle({ user, repo, dependency }, { server, version = '' }) {
+ const allData = await this.fetch({
+ user,
+ repo,
+ schema: allVersionsSchema,
+ server,
+ })
+
+ const { dependencyVersion } = await this.getDependencyVersion({
+ json: allData,
+ user,
+ repo,
+ dependency,
+ version,
+ server,
+ })
+
+ return this.constructor.render({
+ dependency,
+ dependencyVersion,
+ })
+ }
+
+ findVersionIndex(json, version) {
+ return json.findIndex(v => v.version === version)
+ }
+
+ async findSpecifiedVersion(json, user, repo, version, server) {
+ let release
+
+ if ((release = json[this.findVersionIndex(json, version)])) {
+ return release
+ } else {
+ try {
+ const allData = await this.fetchDev({
+ user,
+ repo,
+ schema: allVersionsSchema,
+ server,
+ })
+
+ const versions = BasePackagistService.expandPackageVersions(
+ allData,
+ this.getPackageName(user, repo),
+ )
+
+ return versions[this.findVersionIndex(versions, version)]
+ } catch (e) {
+ return release
+ }
+ }
+ }
+}
diff --git a/services/packagist/packagist-dependency-version.spec.js b/services/packagist/packagist-dependency-version.spec.js
new file mode 100644
index 0000000000000..1f1cde2eafccf
--- /dev/null
+++ b/services/packagist/packagist-dependency-version.spec.js
@@ -0,0 +1,92 @@
+import { expect, use } from 'chai'
+import chaiAsPromised from 'chai-as-promised'
+import PackagistDependencyVersion from './packagist-dependency-version.service.js'
+use(chaiAsPromised)
+
+describe('PackagistDependencyVersion', function () {
+ const fullPackagistJson = {
+ packages: {
+ 'frodo/the-one-package': [
+ {
+ version: 'v3.0.0',
+ require: { php: '^7.4 || 8', 'twig/twig': '~1.28|~2.0' },
+ },
+ {
+ version: 'v2.5.0',
+ require: '__unset',
+ },
+ {
+ version: 'v2.4.0',
+ },
+ {
+ version: 'v2.0.0',
+ require: { php: '^7.2', 'twig/twig': '~1.20|~1.30' },
+ },
+ {
+ version: 'v1.0.0',
+ require: { php: '^5.6 || ^7', 'twig/twig': '~1.10|~1.0' },
+ },
+ ],
+ },
+ }
+
+ it('should throw NotFound when package version is missing in the response', async function () {
+ await expect(
+ PackagistDependencyVersion.prototype.getDependencyVersion({
+ json: fullPackagistJson,
+ user: 'frodo',
+ repo: 'the-one-package',
+ version: 'v4.0.0',
+ }),
+ ).to.be.rejectedWith('invalid version')
+ })
+
+ it('should throw NotFound when `require` section is missing in the response', async function () {
+ await expect(
+ PackagistDependencyVersion.prototype.getDependencyVersion({
+ json: fullPackagistJson,
+ user: 'frodo',
+ repo: 'the-one-package',
+ version: 'v2.4.0',
+ }),
+ ).to.be.rejectedWith('version requirement not found')
+ })
+
+ it('should throw NotFound when `require` section in the response has the value of __unset (thank you, Packagist API :p)', async function () {
+ await expect(
+ PackagistDependencyVersion.prototype.getDependencyVersion({
+ json: fullPackagistJson,
+ user: 'frodo',
+ repo: 'the-one-package',
+ version: 'v2.5.0',
+ }),
+ ).to.be.rejectedWith('version requirement not found')
+ })
+
+ it('should return dependency version for the default release', async function () {
+ expect(
+ await PackagistDependencyVersion.prototype.getDependencyVersion({
+ json: fullPackagistJson,
+ user: 'frodo',
+ repo: 'the-one-package',
+ dependency: 'twig/twig',
+ }),
+ )
+ .to.have.property('dependencyVersion')
+ .that.equals('~1.28|~2.0')
+ })
+
+ it('should return dependency version for the specified release', async function () {
+ expect(
+ await PackagistDependencyVersion.prototype.getDependencyVersion({
+ json: fullPackagistJson,
+ user: 'frodo',
+ repo: 'the-one-package',
+ version: 'v2.0.0',
+ dependency: 'twig/twig',
+ }),
+ )
+ .to.have.property('dependencyVersion')
+ .that.equals('~1.20|~1.30')
+ })
+})
diff --git a/services/packagist/packagist-dependency-version.tester.js b/services/packagist/packagist-dependency-version.tester.js
new file mode 100644
index 0000000000000..3509ae484b5fa
--- /dev/null
+++ b/services/packagist/packagist-dependency-version.tester.js
@@ -0,0 +1,61 @@
+import { isComposerVersion } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('gets the package version')
+ .get('/symfony/symfony/twig/twig.json')
+ .expectBadge({ label: 'twig/twig', message: isComposerVersion })
+
+t.create('incorrect dependency name')
+ .get('/symfony/symfony/twig/twiiiiiiig.json')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'version requirement not found',
+ })
+
+t.create('missing vendor of dependency')
+ .get('/symfony/symfony/twig.json')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'version requirement not found',
+ })
+
+t.create('gets the package version + specified symfony version')
+ .get('/symfony/symfony/twig/twig.json?version=v3.2.8')
+ .expectBadge({ label: 'twig/twig', message: isComposerVersion })
+
+t.create('gets the package version + valid custom server')
+ .get('/symfony/symfony/twig/twig.json?server=https://packagist.org')
+ .expectBadge({ label: 'twig/twig', message: isComposerVersion })
+
+t.create('invalid custom server')
+ .get('/symfony/symfony/twig/twig.json?server=https://packagisttttttt.org')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'inaccessible',
+ })
+
+t.create('incorrect symfony version')
+ .get('/symfony/symfony/twig/twig.json?version=v3.2.80000')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'invalid version',
+ })
+
+t.create('gets the package version - dependency does not need the vendor')
+ .get('/symfony/symfony/ext-xml.json')
+ .expectBadge({ label: 'ext-xml', message: isComposerVersion })
+
+t.create('package with no requirements')
+ .get('/bpampuch/pdfmake/twig/twig.json')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'version requirement not found',
+ })
+
+t.create('package with no twig/twig version requirement')
+ .get('/raulfraile/ladybug-theme-modern/twig/twig.json')
+ .expectBadge({
+ label: 'dependency version',
+ message: 'version requirement not found',
+ })
diff --git a/services/packagist/packagist-downloads.service.js b/services/packagist/packagist-downloads.service.js
index aff7a321d1271..4285d40c704e7 100644
--- a/services/packagist/packagist-downloads.service.js
+++ b/services/packagist/packagist-downloads.service.js
@@ -1,28 +1,25 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { optionalUrl } = require('../validators')
-const {
- keywords,
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { optionalUrl } from '../validators.js'
+import { pathParam, queryParam } from '../index.js'
+import {
BasePackagistService,
customServerDocumentationFragment,
cacheDocumentationFragment,
-} = require('./packagist-base')
+ description,
+} from './packagist-base.js'
const periodMap = {
dm: {
field: 'monthly',
- suffix: '/month',
+ interval: 'month',
},
dd: {
field: 'daily',
- suffix: '/day',
+ interval: 'day',
},
dt: {
field: 'total',
- suffix: '',
},
}
@@ -40,74 +37,59 @@ const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class PackagistDownloads extends BasePackagistService {
- static get category() {
- return 'downloads'
- }
+export default class PackagistDownloads extends BasePackagistService {
+ static category = 'downloads'
- static get route() {
- return {
- base: 'packagist',
- pattern: ':interval(dm|dd|dt)/:user/:repo',
- queryParamSchema,
- }
+ static route = {
+ base: 'packagist',
+ pattern: ':interval(dd|dm|dt)/:user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Packagist',
- namedParams: {
- interval: 'dm',
- user: 'doctrine',
- repo: 'orm',
- },
- staticPreview: this.render({
- downloads: 1000000,
- interval: 'dm',
- }),
- keywords,
- documentation: cacheDocumentationFragment,
+ static openApi = {
+ '/packagist/{interval}/{user}/{repo}': {
+ get: {
+ summary: 'Packagist Downloads',
+ description: description + cacheDocumentationFragment,
+ parameters: [
+ pathParam({
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Monthly, or Total downloads',
+ }),
+ pathParam({
+ name: 'user',
+ example: 'guzzlehttp',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'guzzle',
+ }),
+ queryParam({
+ name: 'server',
+ description: customServerDocumentationFragment,
+ example: 'https://packagist.org',
+ }),
+ ],
},
- {
- title: 'Packagist (custom server)',
- namedParams: {
- interval: 'dm',
- user: 'doctrine',
- repo: 'orm',
- },
- staticPreview: this.render({
- downloads: 1000000,
- interval: 'dm',
- }),
- queryParams: { server: 'https://packagist.org' },
- keywords,
- documentation:
- customServerDocumentationFragment + cacheDocumentationFragment,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'downloads',
- }
+ },
}
- static render({ downloads, interval }) {
- return {
- message: metric(downloads) + periodMap[interval].suffix,
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
- async handle({ interval, user, repo }, { server }) {
+ async handle({ interval: period, user, repo }, { server }) {
const {
package: { downloads },
- } = await this.fetchByJsonAPI({ user, repo, schema, server })
-
- return this.constructor.render({
- downloads: downloads[periodMap[interval].field],
+ } = await this.fetchByJsonAPI({
+ user,
+ repo,
+ schema,
+ server,
+ })
+ const { interval, field } = periodMap[period]
+ return renderDownloadsBadge({
+ downloads: downloads[field],
interval,
})
}
diff --git a/services/packagist/packagist-downloads.tester.js b/services/packagist/packagist-downloads.tester.js
index cd23f75723241..fa0a255ae21fb 100644
--- a/services/packagist/packagist-downloads.tester.js
+++ b/services/packagist/packagist-downloads.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('daily downloads (valid, no package version specified)')
.get('/dd/doctrine/orm.json')
@@ -18,7 +17,7 @@ t.create('daily downloads (valid, no package version specified, custom server)')
})
t.create(
- 'daily downloads (invalid, no package version specified, invalid custom server)'
+ 'daily downloads (invalid, no package version specified, invalid custom server)',
)
.get('/dd/doctrine/orm.json?server=https%3A%2F%2Fpackagist.com')
.expectBadge({
@@ -34,7 +33,7 @@ t.create('monthly downloads (valid, no package version specified)')
})
t.create(
- 'monthly downloads (valid, no package version specified, custom server)'
+ 'monthly downloads (valid, no package version specified, custom server)',
)
.get('/dm/doctrine/orm.json?server=https%3A%2F%2Fpackagist.org')
.expectBadge({
@@ -43,7 +42,7 @@ t.create(
})
t.create(
- 'monthly downloads (valid, no package version specified, invalid custom server)'
+ 'monthly downloads (valid, no package version specified, invalid custom server)',
)
.get('/dm/doctrine/orm.json?server=https%3A%2F%2Fpackagist.com')
.expectBadge({
@@ -66,7 +65,7 @@ t.create('total downloads (valid, no package version specified, custom server)')
})
t.create(
- 'total downloads (valid, no package version specified, invalid custom server)'
+ 'total downloads (valid, no package version specified, invalid custom server)',
)
.get('/dt/doctrine/orm.json?server=https%3A%2F%2Fpackagist.com')
.expectBadge({
@@ -88,7 +87,7 @@ t.create('monthly downloads (invalid, package version in request)')
.expectBadge({ label: '404', message: 'badge not found' })
t.create(
- 'monthly downloads (invalid, package version in request, custom server)'
+ 'monthly downloads (invalid, package version in request, custom server)',
)
.get('/dm/symfony/symfony/v2.8.0.json?server=https%3A%2F%2Fpackagist.org')
.expectBadge({ label: '404', message: 'badge not found' })
diff --git a/services/packagist/packagist-license.service.js b/services/packagist/packagist-license.service.js
index fcc1350f97355..b3c644d7e55f4 100644
--- a/services/packagist/packagist-license.service.js
+++ b/services/packagist/packagist-license.service.js
@@ -1,85 +1,89 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderLicenseBadge } = require('../licenses')
-const { optionalUrl } = require('../validators')
-const { NotFound } = require('..')
-const {
- keywords,
+import Joi from 'joi'
+import { renderLicenseBadge } from '../licenses.js'
+import { optionalUrl } from '../validators.js'
+import { NotFound, pathParam, queryParam } from '../index.js'
+import {
BasePackagistService,
customServerDocumentationFragment,
-} = require('./packagist-base')
+ description,
+} from './packagist-base.js'
-const packageSchema = Joi.object()
- .pattern(
- /^/,
+const packageSchema = Joi.array()
+ .items(
Joi.object({
- license: Joi.array().required(),
- }).required()
+ version: Joi.string(),
+ license: Joi.array(),
+ }).required(),
)
.required()
const schema = Joi.object({
- packages: Joi.object()
- .pattern(/^/, packageSchema)
- .required(),
+ packages: Joi.object().pattern(/^/, packageSchema).required(),
}).required()
const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class PackagistLicense extends BasePackagistService {
- static get category() {
- return 'license'
- }
+export default class PackagistLicense extends BasePackagistService {
+ static category = 'license'
- static get route() {
- return {
- base: 'packagist/l',
- pattern: ':user/:repo',
- queryParamSchema,
- }
+ static route = {
+ base: 'packagist/l',
+ pattern: ':user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Packagist',
- namedParams: { user: 'doctrine', repo: 'orm' },
- staticPreview: renderLicenseBadge({ license: 'MIT' }),
- keywords,
+ static openApi = {
+ '/packagist/l/{user}/{repo}': {
+ get: {
+ summary: 'Packagist License',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'guzzlehttp',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'guzzle',
+ }),
+ queryParam({
+ name: 'server',
+ description: customServerDocumentationFragment,
+ example: 'https://packagist.org',
+ }),
+ ],
},
- {
- title: 'Packagist (custom server)',
- namedParams: { user: 'doctrine', repo: 'orm' },
- queryParams: { server: 'https://packagist.org' },
- staticPreview: renderLicenseBadge({ license: 'MIT' }),
- keywords,
- documentation: customServerDocumentationFragment,
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'license',
- }
+ static defaultBadgeData = {
+ label: 'license',
}
transform({ json, user, repo }) {
const packageName = this.getPackageName(user, repo)
- const branch = json.packages[packageName]['dev-master']
- if (!branch) {
- throw new NotFound({ prettyMessage: 'default branch not found' })
+
+ const versions = BasePackagistService.expandPackageVersions(
+ json,
+ packageName,
+ )
+
+ const version = this.findLatestRelease(versions)
+ const license = version.license
+ if (!license) {
+ throw new NotFound({ prettyMessage: 'license not found' })
}
- const { license } = branch
+
return { license }
}
async handle({ user, repo }, { server }) {
const json = await this.fetch({ user, repo, schema, server })
+
const { license } = this.transform({ json, user, repo })
+
return renderLicenseBadge({ license })
}
}
diff --git a/services/packagist/packagist-license.spec.js b/services/packagist/packagist-license.spec.js
index 315b6cf9e9785..5355681f2175e 100644
--- a/services/packagist/packagist-license.spec.js
+++ b/services/packagist/packagist-license.spec.js
@@ -1,27 +1,111 @@
-'use strict'
+import { expect } from 'chai'
+import { NotFound } from '../index.js'
+import PackagistLicense from './packagist-license.service.js'
-const { expect } = require('chai')
-const { NotFound } = require('..')
-const PackagistLicense = require('./packagist-license.service')
+describe('PackagistLicense', function () {
+ it('should return the license of the most recent release', function () {
+ const json = {
+ packages: {
+ 'frodo/the-one-package': [
+ {
+ version: '1.2.4',
+ license: 'MIT-latest',
+ },
+ {
+ version: '1.2.3',
+ license: 'MIT',
+ },
+ ],
+ },
+ }
+
+ expect(
+ PackagistLicense.prototype.transform({
+ json,
+ user: 'frodo',
+ repo: 'the-one-package',
+ }),
+ )
+ .to.have.property('license')
+ .that.equals('MIT-latest')
+ })
+
+ it('should return the license of the most recent stable release', function () {
+ const json = {
+ packages: {
+ 'frodo/the-one-package': [
+ {
+ version: '1.2.4-RC1', // Pre-release
+ license: 'MIT-latest',
+ },
+ {
+ version: '1.2.3', // Stable release
+ license: 'MIT',
+ },
+ ],
+ },
+ }
+
+ expect(
+ PackagistLicense.prototype.transform({
+ json,
+ user: 'frodo',
+ repo: 'the-one-package',
+ }),
+ )
+ .to.have.property('license')
+ .that.equals('MIT')
+ })
-describe('PackagistLicense', function() {
- it('should throw NotFound when default branch is missing', function() {
+ it('should return the license of the most recent pre-release if no stable releases', function () {
const json = {
packages: {
- 'doctrine/orm': {},
- 'elhadraoui/doctrine-orm': {
- 'dev-master': { license: 'MIT' },
- },
+ 'frodo/the-one-package': [
+ {
+ version: '1.2.4-RC2',
+ license: 'MIT-latest',
+ },
+ {
+ version: '1.2.4-RC1',
+ license: 'MIT',
+ },
+ ],
},
}
+
+ expect(
+ PackagistLicense.prototype.transform({
+ json,
+ user: 'frodo',
+ repo: 'the-one-package',
+ }),
+ )
+ .to.have.property('license')
+ .that.equals('MIT-latest')
+ })
+
+ it('should throw NotFound when license key not in response', function () {
+ const json = {
+ packages: {
+ 'frodo/the-one-package': [
+ {
+ version: '1.2.4',
+ },
+ {
+ version: '1.2.3',
+ },
+ ],
+ },
+ }
+
expect(() =>
PackagistLicense.prototype.transform({
json,
- user: 'doctrine',
- repo: 'orm',
- })
+ user: 'frodo',
+ repo: 'the-one-package',
+ }),
)
.to.throw(NotFound)
- .with.property('prettyMessage', 'default branch not found')
+ .with.property('prettyMessage', 'license not found')
})
})
diff --git a/services/packagist/packagist-license.tester.js b/services/packagist/packagist-license.tester.js
index 983da773e4f9b..e43a5bf3d96f1 100644
--- a/services/packagist/packagist-license.tester.js
+++ b/services/packagist/packagist-license.tester.js
@@ -1,13 +1,12 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license (valid)')
.get('/symfony/symfony.json')
.expectBadge({ label: 'license', message: 'MIT' })
// note: packagist does serve up license at the version level
-// but our endpoint only supports fetching license for the lastest version
+// but our endpoint only supports fetching license for the latest version
t.create('license (invalid, package version in request)')
.get('/symfony/symfony/v2.8.0.json')
.expectBadge({ label: '404', message: 'badge not found' })
diff --git a/services/packagist/packagist-php-version.service.js b/services/packagist/packagist-php-version.service.js
index b775b6d51d07e..177d94bc94f17 100644
--- a/services/packagist/packagist-php-version.service.js
+++ b/services/packagist/packagist-php-version.service.js
@@ -1,101 +1,21 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { NotFound } = require('..')
-const {
- allVersionsSchema,
- BasePackagistService,
- customServerDocumentationFragment,
-} = require('./packagist-base')
+import Joi from 'joi'
+import { redirector } from '../index.js'
+import { optionalUrl } from '../validators.js'
const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class PackagistPhpVersion extends BasePackagistService {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return {
- base: 'packagist/php-v',
- pattern: ':user/:repo/:version?',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'PHP from Packagist',
- pattern: ':user/:repo',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- },
- staticPreview: this.render({ php: '^7.1.3' }),
- },
- {
- title: 'PHP from Packagist (specify version)',
- pattern: ':user/:repo/:version',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- version: 'v2.8.0',
- },
- staticPreview: this.render({ php: '>=5.3.9' }),
- },
- {
- title: 'PHP from Packagist (custom server)',
- pattern: ':user/:repo',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- },
- queryParams: {
- server: 'https://packagist.org',
- },
- staticPreview: this.render({ php: '^7.1.3' }),
- documentation: customServerDocumentationFragment,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'php',
- color: 'blue',
- }
- }
-
- static render({ php }) {
- return {
- message: php,
- }
- }
-
- async handle({ user, repo, version = 'dev-master' }, { server }) {
- const allData = await this.fetch({
- user,
- repo,
- schema: allVersionsSchema,
- server,
- })
-
- if (!(version in allData.packages[this.getPackageName(user, repo)])) {
- throw new NotFound({ prettyMessage: 'invalid version' })
- }
-
- const packageVersion =
- allData.packages[this.getPackageName(user, repo)][version]
- if (!packageVersion.require || !packageVersion.require.php) {
- throw new NotFound({ prettyMessage: 'version requirement not found' })
- }
-
- return this.constructor.render({
- php: packageVersion.require.php,
- })
- }
-}
+export default redirector({
+ category: 'platform-support',
+ route: {
+ base: 'packagist/php-v',
+ pattern: ':user/:repo/:version?',
+ queryParamSchema,
+ },
+ transformPath: ({ user, repo }) =>
+ `/packagist/dependency-v/${user}/${repo}/php`,
+ transformQueryParams: ({ version, server }) => ({ version, server }),
+ overrideTransformedQueryParams: true,
+ dateAdded: new Date('2022-09-07'),
+})
diff --git a/services/packagist/packagist-php-version.tester.js b/services/packagist/packagist-php-version.tester.js
index 0aa385908892e..0d3a997d21563 100644
--- a/services/packagist/packagist-php-version.tester.js
+++ b/services/packagist/packagist-php-version.tester.js
@@ -1,36 +1,24 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isComposerVersion } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('gets the package version of symfony')
+t.create(
+ 'redirect getting required php version for the dependency from packagist (valid, package version not specified in request)',
+)
.get('/symfony/symfony.json')
- .expectBadge({ label: 'php', message: isComposerVersion })
-
-t.create('gets the package version of symfony 2.8')
- .get('/symfony/symfony/v2.8.0.json')
- .expectBadge({ label: 'php', message: isComposerVersion })
-
-t.create('package with no requirements')
- .get('/bpampuch/pdfmake.json')
- .expectBadge({ label: 'php', message: 'version requirement not found' })
-
-t.create('package with no php version requirement')
- .get('/raulfraile/ladybug-theme-modern.json')
- .expectBadge({ label: 'php', message: 'version requirement not found' })
-
-t.create('invalid package name')
- .get('/frodo/is-not-a-package.json')
- .expectBadge({ label: 'php', message: 'not found' })
-
-t.create('invalid version')
- .get('/symfony/symfony/invalid.json')
- .expectBadge({ label: 'php', message: 'invalid version' })
-
-t.create('custom server')
- .get('/symfony/symfony.json?server=https%3A%2F%2Fpackagist.org')
- .expectBadge({ label: 'php', message: isComposerVersion })
-
-t.create('invalid custom server')
- .get('/symfony/symfony.json?server=https%3A%2F%2Fpackagist.com')
- .expectBadge({ label: 'php', message: 'not found' })
+ .expectRedirect('/packagist/dependency-v/symfony/symfony/php.json?')
+
+t.create(
+ 'redirect getting required php version for the dependency from packagist (valid, package version specified in request)',
+)
+ .get('/symfony/symfony/v3.2.8.json')
+ .expectRedirect(
+ '/packagist/dependency-v/symfony/symfony/php.json?version=v3.2.8',
+ )
+
+t.create(
+ 'redirect getting required php version for the dependency from packagist (valid, package version and server specified in request)',
+)
+ .get('/symfony/symfony/v3.2.8.json?server=https://packagist.org')
+ .expectRedirect(
+ '/packagist/dependency-v/symfony/symfony/php.json?version=v3.2.8&server=https%3A%2F%2Fpackagist.org',
+ )
diff --git a/services/packagist/packagist-stars.service.js b/services/packagist/packagist-stars.service.js
new file mode 100644
index 0000000000000..5c32849aa07a4
--- /dev/null
+++ b/services/packagist/packagist-stars.service.js
@@ -0,0 +1,75 @@
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger, optionalUrl } from '../validators.js'
+import { pathParam, queryParam } from '../index.js'
+import {
+ BasePackagistService,
+ customServerDocumentationFragment,
+ cacheDocumentationFragment,
+ description,
+} from './packagist-base.js'
+
+const schema = Joi.object({
+ package: Joi.object({
+ favers: nonNegativeInteger,
+ }).required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ server: optionalUrl,
+}).required()
+
+export default class PackagistStars extends BasePackagistService {
+ static category = 'rating'
+
+ static route = {
+ base: 'packagist/stars',
+ pattern: ':user/:repo',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/packagist/stars/{user}/{repo}': {
+ get: {
+ summary: 'Packagist Stars',
+ description: description + cacheDocumentationFragment,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'guzzlehttp',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'guzzle',
+ }),
+ queryParam({
+ name: 'server',
+ description: customServerDocumentationFragment,
+ example: 'https://packagist.org',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'stars',
+ }
+
+ static render({ stars }) {
+ return {
+ message: metric(stars),
+ color: 'brightgreen',
+ }
+ }
+
+ async handle({ user, repo }, { server }) {
+ const {
+ package: { favers },
+ } = await this.fetchByJsonAPI({ user, repo, schema, server })
+
+ return this.constructor.render({
+ stars: favers,
+ })
+ }
+}
diff --git a/services/packagist/packagist-stars.tester.js b/services/packagist/packagist-stars.tester.js
new file mode 100644
index 0000000000000..dccd23629f390
--- /dev/null
+++ b/services/packagist/packagist-stars.tester.js
@@ -0,0 +1,29 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Stars (valid package)').get('/guzzlehttp/guzzle.json').expectBadge({
+ label: 'stars',
+ message: isMetric,
+})
+
+t.create('Stars (invalid package)')
+ .get('/frodo/is-not-a-package.json')
+ .expectBadge({
+ label: 'stars',
+ message: 'not found',
+ })
+
+t.create('Stars (valid package, custom server)')
+ .get('/guzzlehttp/guzzle.json?server=https%3A%2F%2Fpackagist.org')
+ .expectBadge({
+ label: 'stars',
+ message: isMetric,
+ })
+
+t.create('Stars (invalid package, custom server)')
+ .get('/frodo/is-not-a-package.json?server=https%3A%2F%2Fpackagist.org')
+ .expectBadge({
+ label: 'stars',
+ message: 'not found',
+ })
diff --git a/services/packagist/packagist-version.service.js b/services/packagist/packagist-version.service.js
index 8d4c88e6abbb9..aafd1256c1535 100644
--- a/services/packagist/packagist-version.service.js
+++ b/services/packagist/packagist-version.service.js
@@ -1,33 +1,21 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { compare, isStable, latest } = require('../php-version')
-const { optionalUrl } = require('../validators')
-const { NotFound, redirector } = require('..')
-const {
- allVersionsSchema,
- keywords,
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { optionalUrl } from '../validators.js'
+import { redirector, pathParam, queryParam } from '../index.js'
+import {
BasePackagistService,
customServerDocumentationFragment,
-} = require('./packagist-base')
+ description,
+} from './packagist-base.js'
-const packageSchema = Joi.object()
- .pattern(
- /^/,
- Joi.object({
- version: Joi.string(),
- extra: Joi.object({
- 'branch-alias': Joi.object().pattern(/^/, Joi.string()),
- }),
- }).required()
- )
- .required()
+const packageSchema = Joi.array().items(
+ Joi.object({
+ version: Joi.string().required(),
+ }),
+)
const schema = Joi.object({
- packages: Joi.object()
- .pattern(/^/, packageSchema)
- .required(),
+ packages: Joi.object().pattern(/^/, packageSchema).required(),
}).required()
const queryParamSchema = Joi.object({
@@ -36,110 +24,64 @@ const queryParamSchema = Joi.object({
}).required()
class PackagistVersion extends BasePackagistService {
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: 'packagist/v',
- pattern: ':user/:repo',
- queryParamSchema,
- }
+ static route = {
+ base: 'packagist/v',
+ pattern: ':user/:repo',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Packagist Version',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- },
- staticPreview: renderVersionBadge({ version: '4.2.2' }),
- keywords,
- },
- {
- title: 'Packagist Version (including pre-releases)',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- },
- queryParams: { include_prereleases: null },
- staticPreview: renderVersionBadge({ version: '4.3-dev' }),
- keywords,
- },
- {
- title: 'Packagist Version (custom server)',
- namedParams: {
- user: 'symfony',
- repo: 'symfony',
- },
- queryParams: {
- server: 'https://packagist.org',
- },
- staticPreview: renderVersionBadge({ version: '4.2.2' }),
- keywords,
- documentation: customServerDocumentationFragment,
+ static openApi = {
+ '/packagist/v/{user}/{repo}': {
+ get: {
+ summary: 'Packagist Version',
+ description,
+ parameters: [
+ pathParam({
+ name: 'user',
+ example: 'symfony',
+ }),
+ pathParam({
+ name: 'repo',
+ example: 'symfony',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ queryParam({
+ name: 'server',
+ description: customServerDocumentationFragment,
+ example: 'https://packagist.org',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'packagist',
- }
+ static defaultBadgeData = {
+ label: 'packagist',
}
static render({ version }) {
- if (version === undefined) {
- throw new NotFound({ prettyMessage: 'no released version found' })
- }
return renderVersionBadge({ version })
}
- transform({ includePrereleases, json, user, repo }) {
- const versionsData = json.packages[this.getPackageName(user, repo)]
- let versions = Object.keys(versionsData)
- const aliasesMap = {}
- versions.forEach(version => {
- const versionData = versionsData[version]
- if (
- versionData.extra &&
- versionData.extra['branch-alias'] &&
- versionData.extra['branch-alias'][version]
- ) {
- // eg, version is 'dev-master', mapped to '2.0.x-dev'.
- const validVersion = versionData.extra['branch-alias'][version]
- if (
- aliasesMap[validVersion] === undefined ||
- compare(aliasesMap[validVersion], validVersion) < 0
- ) {
- versions.push(validVersion)
- aliasesMap[validVersion] = version
- }
- }
- })
-
- versions = versions.filter(version => !/^dev-/.test(version))
-
- if (includePrereleases) {
- return { version: latest(versions) }
- } else {
- const stableVersion = latest(versions.filter(isStable))
- return { version: stableVersion || latest(versions) }
- }
- }
-
- async handle({ user, repo }, { include_prereleases, server }) {
- const includePrereleases = include_prereleases !== undefined
+ async handle(
+ { user, repo },
+ { include_prereleases: includePrereleases, server },
+ ) {
+ includePrereleases = includePrereleases !== undefined
const json = await this.fetch({
user,
repo,
- schema: includePrereleases ? schema : allVersionsSchema,
+ schema,
server,
})
- const { version } = this.transform({ includePrereleases, json, user, repo })
+ const versions = json.packages[this.getPackageName(user, repo)]
+ const { version } = this.findLatestRelease(versions, includePrereleases)
return this.constructor.render({ version })
}
}
@@ -155,4 +97,4 @@ const PackagistVersionRedirector = redirector({
dateAdded: new Date('2019-12-15'),
})
-module.exports = { PackagistVersion, PackagistVersionRedirector }
+export { PackagistVersion, PackagistVersionRedirector }
diff --git a/services/packagist/packagist-version.tester.js b/services/packagist/packagist-version.tester.js
index eff84fc48717d..bc115fbb90d6b 100644
--- a/services/packagist/packagist-version.tester.js
+++ b/services/packagist/packagist-version.tester.js
@@ -1,15 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import Joi from 'joi'
+import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'packagist',
title: 'Packagist Version',
pathPrefix: '/packagist',
-}))
+})
/*
validator for a packagist version number
@@ -20,27 +16,23 @@ const t = (module.exports = new ServiceTester({
*/
const isPackagistVersion = Joi.string().regex(/^v?[0-9]+.[0-9]+.[0-9]+[\S]*$/)
-t.create('version (valid)')
- .get('/v/symfony/symfony.json')
- .expectBadge({
- label: 'packagist',
- message: isPackagistVersion,
- })
+t.create('version (valid)').get('/v/symfony/symfony.json').expectBadge({
+ label: 'packagist',
+ message: isPackagistVersion,
+})
-t.create('version (no releases)')
- .get('/v/wsg/hello.json')
- .expectBadge({
- label: 'packagist',
- message: 'no released version found',
- color: 'red',
- })
+t.create('version (no releases)').get('/v/wsg/hello.json').expectBadge({
+ label: 'packagist',
+ message: 'no released version found',
+ color: 'red',
+})
t.create('version (invalid package name)')
.get('/v/frodo/is-not-a-package.json')
.expectBadge({ label: 'packagist', message: 'not found' })
t.create('pre-release version (valid)')
- .get('/v/symfony/symfony.json?include_prereleases')
+ .get('/v/guzzlehttp/guzzle.json?include_prereleases')
.expectBadge({
label: 'packagist',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
@@ -64,5 +56,5 @@ t.create('version (legacy redirect: vpre)')
t.create('version (legacy redirect: vpre) (custom server)')
.get('/vpre/symfony/symfony.svg?server=https%3A%2F%2Fpackagist.org')
.expectRedirect(
- '/packagist/v/symfony/symfony.svg?include_prereleases&server=https%3A%2F%2Fpackagist.org'
+ '/packagist/v/symfony/symfony.svg?server=https%3A%2F%2Fpackagist.org&include_prereleases',
)
diff --git a/services/pepy/pepy-downloads.service.js b/services/pepy/pepy-downloads.service.js
new file mode 100644
index 0000000000000..305bf4fbf0cc9
--- /dev/null
+++ b/services/pepy/pepy-downloads.service.js
@@ -0,0 +1,61 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+
+const schema = Joi.object({
+ total_downloads: nonNegativeInteger,
+}).required()
+
+const description = `
+Python package total downloads from [Pepy](https://www.pepy.tech/).
+
+Download stats from pepy count package downloads from PyPI and known mirrors.`
+
+export default class PepyDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'pepy',
+ pattern: 'dt/:packageName',
+ }
+
+ static auth = {
+ passKey: 'pepy_key',
+ authorizedOrigins: ['https://api.pepy.tech'],
+ isRequired: true,
+ }
+
+ static openApi = {
+ '/pepy/dt/{packageName}': {
+ get: {
+ summary: 'Pepy Total Downloads',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'django',
+ }),
+ },
+ },
+ }
+
+ static _cacheLength = 28800
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async fetch({ packageName }) {
+ return this._requestJson(
+ this.authHelper.withApiKeyHeader({
+ url: `https://api.pepy.tech/api/v2/projects/${packageName}`,
+ schema,
+ }),
+ )
+ }
+
+ async handle({ packageName }) {
+ const data = await this.fetch({ packageName })
+ return renderDownloadsBadge({
+ downloads: data.total_downloads,
+ })
+ }
+}
diff --git a/services/pepy/pepy-downloads.spec.js b/services/pepy/pepy-downloads.spec.js
new file mode 100644
index 0000000000000..9a529af63cf1f
--- /dev/null
+++ b/services/pepy/pepy-downloads.spec.js
@@ -0,0 +1,10 @@
+import { testAuth } from '../test-helpers.js'
+import PepyDownloads from './pepy-downloads.service.js'
+
+describe('PepyDownloads', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(PepyDownloads, 'ApiKeyHeader', { total_downloads: 42 })
+ })
+ })
+})
diff --git a/services/pepy/pepy-downloads.tester.js b/services/pepy/pepy-downloads.tester.js
new file mode 100644
index 0000000000000..a90172f7b1073
--- /dev/null
+++ b/services/pepy/pepy-downloads.tester.js
@@ -0,0 +1,11 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('downloads (valid)')
+ .get('/dt/django.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('downloads (not found)')
+ .get('/dt/not-a-package.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
diff --git a/services/php-eye/php-eye-hhvm.service.js b/services/php-eye/php-eye-hhvm.service.js
deleted file mode 100644
index f157de1eeb853..0000000000000
--- a/services/php-eye/php-eye-hhvm.service.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = [
- deprecatedService({
- category: 'platform-support',
- label: 'hhvm',
- route: {
- base: 'hhvm',
- pattern: ':various*',
- },
- dateAdded: new Date('2018-04-20'),
- }),
-]
diff --git a/services/php-eye/php-eye-hhvm.tester.js b/services/php-eye/php-eye-hhvm.tester.js
deleted file mode 100644
index ad4e58852df34..0000000000000
--- a/services/php-eye/php-eye-hhvm.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'hhvm',
- title: 'hhvm',
- pathPrefix: '/hhvm',
-}))
-
-t.create('no longer available (previously default branch)')
- .get('/symfony/symfony.json')
- .expectBadge({
- label: 'hhvm',
- message: 'no longer available',
- })
-
-t.create('no longer available (get specific branch)')
- .get('/yiisoft/yii/1.1.19.json')
- .expectBadge({
- label: 'hhvm',
- message: 'no longer available',
- })
diff --git a/services/php-eye/php-eye-php-version.service.js b/services/php-eye/php-eye-php-version.service.js
deleted file mode 100644
index e7f13fb857ba1..0000000000000
--- a/services/php-eye/php-eye-php-version.service.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = [
- deprecatedService({
- category: 'platform-support',
- label: 'php tested',
- route: {
- base: 'php-eye',
- pattern: ':various*',
- },
- dateAdded: new Date('2018-04-20'),
- }),
-]
diff --git a/services/php-eye/php-eye-php-version.tester.js b/services/php-eye/php-eye-php-version.tester.js
deleted file mode 100644
index ddf754cf481a2..0000000000000
--- a/services/php-eye/php-eye-php-version.tester.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'php-eye',
- title: 'php-eye',
- pathPrefix: '/php-eye',
-}))
-
-t.create('no longer available (previously default branch)')
- .get('/symfony/symfony.json')
- .expectBadge({
- label: 'php tested',
- message: 'no longer available',
- })
-
-t.create('no longer available (get specific branch)')
- .get('/yiisoft/yii/1.1.19.json')
- .expectBadge({
- label: 'php tested',
- message: 'no longer available',
- })
diff --git a/services/php-version.js b/services/php-version.js
index 17ede45a6f46d..5c8927565d447 100644
--- a/services/php-version.js
+++ b/services/php-version.js
@@ -2,17 +2,23 @@
* Utilities relating to PHP version numbers. This compares version numbers
* using the algorithm followed by Composer (see
* https://getcomposer.org/doc/04-schema.md#version).
+ *
+ * @module
*/
-'use strict'
-const { promisify } = require('util')
-const request = require('request')
-const { regularUpdate } = require('../core/legacy/regular-update')
-const { listCompare } = require('./version')
-const { omitv } = require('./text-formatters')
+import { fetch } from '../core/base-service/got.js'
+import { getCachedResource } from '../core/base-service/resource-cache.js'
+import { listCompare } from './version.js'
+import { omitv } from './text-formatters.js'
-// Return a negative value if v1 < v2,
-// zero if v1 = v2, a positive value otherwise.
+/**
+ * Return a negative value if v1 < v2,
+ * zero if v1 = v2, a positive value otherwise.
+ *
+ * @param {string} v1 - First version for comparison
+ * @param {string} v2 - Second version for comparison
+ * @returns {number} Comparison result (-1, 0 or 1)
+ */
function asciiVersionCompare(v1, v2) {
if (v1 < v2) {
return -1
@@ -23,83 +29,61 @@ function asciiVersionCompare(v1, v2) {
}
}
-// Take a version without the starting v.
-// eg, '1.0.x-beta'
-// Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
+/**
+ * Take a version without the starting v.
+ * eg, '1.0.x-beta'
+ * Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
+ *
+ * @param {string} version - Version number string
+ * @returns {object} Object containing version details
+ */
function numberedVersionData(version) {
- // A version has a numbered part and a modifier part
- // (eg, 1.0.0-patch, 2.0.x-dev).
- const parts = version.split('-')
- const numbered = parts[0]
-
- // Aliases that get caught here.
- if (numbered === 'dev') {
- return {
- numbers: parts[1],
- modifier: 5,
- modifierCount: 1,
- }
+ // https://github.com/composer/semver/blob/46d9139568ccb8d9e7cdd4539cab7347568a5e2e/src/VersionParser.php#L39
+ const regex =
+ /^(\d+(?:\.\d+)*)(?:[._-]?(stable|beta|b|RC|alpha|a|patch|pl|p)?((?:[.-]?\d+)*)?)?([.-]?dev)?$/i
+ const match = version.match(regex)
+
+ if (!match || match.length < 5) {
+ throw new Error(`Unparseable PHP version: ${version}`)
}
- let modifierLevel = 3
+ let modifierLevel = 3 // default: stable/without modifiers
let modifierLevelCount = 0
+ const modifier = match[2] ? match[2].toLowerCase() : ''
+ const modifierCountStr = match[3] || ''
+ const devModifier = match[4] ? match[4].toLowerCase() : ''
+
// Normalization based on
// https://github.com/composer/semver/blob/1.5.0/src/VersionParser.php
+ if (modifier === 'alpha' || modifier === 'a') {
+ modifierLevel = 0
+ modifierLevelCount = +modifierCountStr.replace(/[^\d]/g, '') || 1
+ } else if (modifier === 'beta' || modifier === 'b') {
+ modifierLevel = 1
+ modifierLevelCount = +modifierCountStr.replace(/[^\d]/g, '') || 1
+ } else if (modifier === 'rc') {
+ modifierLevel = 2
+ modifierLevelCount = +modifierCountStr.replace(/[^\d]/g, '') || 1
+ } else if (modifier === 'stable' || modifier === '') {
+ modifierLevel = 3
+ modifierLevelCount = 1
+ } else if (modifier === 'patch' || modifier === 'pl' || modifier === 'p') {
+ modifierLevel = 4
+ modifierLevelCount = +modifierCountStr.replace(/[^\d]/g, '') || 1
+ }
- if (parts.length > 1) {
- const modifier = parts[parts.length - 1]
- const firstLetter = modifier.charCodeAt(0)
- let modifierLevelCountString
-
- // Modifiers: alpha < beta < RC < normal < patch < dev
- if (firstLetter === 97 || firstLetter === 65) {
- // a / A
- modifierLevel = 0
- if (/^alpha/i.test(modifier)) {
- modifierLevelCountString = +modifier.slice(5)
- } else {
- modifierLevelCountString = +modifier.slice(1)
- }
- } else if (firstLetter === 98 || firstLetter === 66) {
- // b / B
- modifierLevel = 1
- if (/^beta/i.test(modifier)) {
- modifierLevelCountString = +modifier.slice(4)
- } else {
- modifierLevelCountString = +modifier.slice(1)
- }
- } else if (firstLetter === 82 || firstLetter === 114) {
- // R / r
- modifierLevel = 2
- modifierLevelCountString = +modifier.slice(2)
- } else if (firstLetter === 112) {
- // p
- modifierLevel = 4
- if (/^patch/.test(modifier)) {
- modifierLevelCountString = +modifier.slice(5)
- } else {
- modifierLevelCountString = +modifier.slice(1)
- }
- } else if (firstLetter === 100) {
- // d
- modifierLevel = 5
- if (/^dev/.test(modifier)) {
- modifierLevelCountString = +modifier.slice(3)
- } else {
- modifierLevelCountString = +modifier.slice(1)
- }
- }
-
- // If we got the empty string, it defaults to a modifier count of 1.
- if (!modifierLevelCountString) {
- modifierLevelCount = 1
- } else {
- modifierLevelCount = +modifierLevelCountString
- }
+ if (devModifier) {
+ modifierLevel = 5
+ modifierLevelCount = 1
}
- // Try to convert to a list of numbers.
+ /**
+ * Try to convert to a list of numbers.
+ *
+ * @param {string} s - Version number string
+ * @returns {number} Version number integer
+ */
function toNum(s) {
let n = +s
if (Number.isNaN(n)) {
@@ -107,7 +91,7 @@ function numberedVersionData(version) {
}
return n
}
- const numberList = numbered.split('.').map(toNum)
+ const numberList = match[1].split('.').map(toNum)
return {
numbers: numberList,
@@ -116,12 +100,15 @@ function numberedVersionData(version) {
}
}
-// Return a negative value if v1 < v2,
-// zero if v1 = v2,
-// a positive value otherwise.
-//
-// See https://getcomposer.org/doc/04-schema.md#version
-// and https://github.com/badges/shields/issues/319#issuecomment-74411045
+/**
+ * Compares two versions and return an integer based on the result.
+ * See https://getcomposer.org/doc/04-schema.md#version
+ * and https://github.com/badges/shields/issues/319#issuecomment-74411045
+ *
+ * @param {string} v1 - First version
+ * @param {string} v2 - Second version
+ * @returns {number} Negative value if v1 < v2, zero if v1 = v2, else a positive value
+ */
function compare(v1, v2) {
// Omit the starting `v`.
const rawv1 = omitv(v1)
@@ -157,6 +144,12 @@ function compare(v1, v2) {
return 0
}
+/**
+ * Determines the latest version from a list of versions.
+ *
+ * @param {string[]} versions - List of versions
+ * @returns {string} Latest version
+ */
function latest(versions) {
let latest = versions[0]
for (let i = 1; i < versions.length; i++) {
@@ -167,6 +160,12 @@ function latest(versions) {
return latest
}
+/**
+ * Determines if a version is stable or not.
+ *
+ * @param {string} version - Version number
+ * @returns {boolean} true if version is stable, else false
+ */
function isStable(version) {
const rawVersion = omitv(version)
let versionData
@@ -179,6 +178,12 @@ function isStable(version) {
return versionData.modifier === 3 || versionData.modifier === 4
}
+/**
+ * Checks if a version is valid and returns the minor version.
+ *
+ * @param {string} version - Version number
+ * @returns {string} Minor version
+ */
function minorVersion(version) {
const result = version.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/)
@@ -189,6 +194,13 @@ function minorVersion(version) {
return `${result[1]}.${result[2] ? result[2] : '0'}`
}
+/**
+ * Reduces the list of php versions that intersect with release versions to a version range (for eg. '5.4 - 7.1', '>= 5.5').
+ *
+ * @param {string[]} versions - List of php versions
+ * @param {string[]} phpReleases - List of php release versions
+ * @returns {string[]} Reduced Version Range (for eg. ['5.4 - 7.1'], ['>= 5.5'])
+ */
function versionReduction(versions, phpReleases) {
if (!versions.length) {
return []
@@ -219,28 +231,33 @@ function versionReduction(versions, phpReleases) {
return versions
}
+/**
+ * Fetches the PHP release versions from cache if exists, else fetch from the source url and save in cache.
+ *
+ * @async
+ * @param {object} githubApiProvider - Github API provider
+ * @returns {Promise<*>} Promise that resolves to parsed response
+ */
async function getPhpReleases(githubApiProvider) {
- return promisify(regularUpdate)({
+ return getCachedResource({
url: '/repos/php/php-src/git/refs/tags',
- intervalMillis: 24 * 3600 * 1000, // 1 day
scraper: tags =>
Array.from(
new Set(
tags
// only releases
.filter(
- tag => tag.ref.match(/^refs\/tags\/php-\d+\.\d+\.\d+$/) != null
+ tag => tag.ref.match(/^refs\/tags\/php-\d+\.\d+\.\d+$/) != null,
)
// get minor version of release
- .map(tag => tag.ref.match(/^refs\/tags\/php-(\d+\.\d+)\.\d+$/)[1])
- )
+ .map(tag => tag.ref.match(/^refs\/tags\/php-(\d+\.\d+)\.\d+$/)[1]),
+ ),
),
- request: (url, options, cb) =>
- githubApiProvider.request(request, url, {}, cb),
+ requestFetcher: githubApiProvider.fetch.bind(githubApiProvider, fetch),
})
}
-module.exports = {
+export {
compare,
latest,
isStable,
diff --git a/services/php-version.spec.js b/services/php-version.spec.js
index ca2390e2be115..3e2e0321545e7 100644
--- a/services/php-version.spec.js
+++ b/services/php-version.spec.js
@@ -1,7 +1,10 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const { compare, minorVersion, versionReduction } = require('./php-version')
+import { test, given } from 'sazerac'
+import {
+ compare,
+ isStable,
+ minorVersion,
+ versionReduction,
+} from './php-version.js'
const phpReleases = [
'5.0',
@@ -16,7 +19,7 @@ const phpReleases = [
'7.2',
]
-describe('Text PHP version', function() {
+describe('Text PHP version', function () {
test(minorVersion, () => {
given('7').expect('7.0')
given('7.1').expect('7.1')
@@ -39,7 +42,7 @@ describe('Text PHP version', function() {
given(['7.0', '7.1', '7.2'], phpReleases).expect(['>= 7'])
given(
['5.0', '5.1', '5.2', '5.3', '5.4', '5.5', '5.6', '7.0', '7.1', '7.2'],
- phpReleases
+ phpReleases,
).expect(['>= 5'])
given(['7.1', '7.2'], phpReleases).expect(['>= 7.1'])
given(['7.1'], phpReleases).expect(['7.1'])
@@ -48,7 +51,7 @@ describe('Text PHP version', function() {
})
})
-describe('Composer version comparison', function() {
+describe('Composer version comparison', function () {
test(compare, () => {
// composer version scheme ordering
given('0.9.0', '1.0.0-alpha').expect(-1)
@@ -84,3 +87,55 @@ describe('Composer version comparison', function() {
given('1.0.0-rc10', '1.0.0-RC11').expect(-1)
})
})
+
+describe('isStable', function () {
+ test(isStable, () => {
+ // dash separator
+ given('1.0.0-alpha').expect(false)
+ given('1.0.0-alpha2').expect(false)
+ given('1.0.0-beta').expect(false)
+ given('1.0.0-beta2').expect(false)
+ given('1.0.0-RC').expect(false)
+ given('1.0.0-RC2').expect(false)
+ given('1.0.0').expect(true)
+ given('1.0.0-patch').expect(true)
+ given('1.0.0-dev').expect(false)
+ given('1.0.x-dev').expect(false)
+
+ // underscore separator
+ given('1.0.0_alpha').expect(false)
+ given('1.0.0_alpha2').expect(false)
+ given('1.0.0_beta').expect(false)
+ given('1.0.0_beta2').expect(false)
+ given('1.0.0_RC').expect(false)
+ given('1.0.0_RC2').expect(false)
+ given('1.0.0').expect(true)
+ given('1.0.0_patch').expect(true)
+ given('1.0.0_dev').expect(false)
+ given('1.0.x_dev').expect(false)
+
+ // dot separator
+ given('1.0.0.alpha').expect(false)
+ given('1.0.0.alpha2').expect(false)
+ given('1.0.0.beta').expect(false)
+ given('1.0.0.beta2').expect(false)
+ given('1.0.0.RC').expect(false)
+ given('1.0.0.RC2').expect(false)
+ given('1.0.0').expect(true)
+ given('1.0.0.patch').expect(true)
+ given('1.0.0.dev').expect(false)
+ given('1.0.x.dev').expect(false)
+
+ // no separator
+ given('1.0.0alpha').expect(false)
+ given('1.0.0alpha2').expect(false)
+ given('1.0.0beta').expect(false)
+ given('1.0.0beta2').expect(false)
+ given('1.0.0RC').expect(false)
+ given('1.0.0RC2').expect(false)
+ given('1.0.0').expect(true)
+ given('1.0.0patch').expect(true)
+ given('1.0.0dev').expect(false)
+ given('1.0.xdev').expect(false)
+ })
+})
diff --git a/services/pingpong/pingpong-base.js b/services/pingpong/pingpong-base.js
new file mode 100644
index 0000000000000..62a517613e696
--- /dev/null
+++ b/services/pingpong/pingpong-base.js
@@ -0,0 +1,22 @@
+import { BaseJsonService, InvalidParameter } from '../index.js'
+
+export const description = `
+[PingPong](https://pingpong.one/) is a status page and monitoring service.
+
+To see more details about this badge and obtain your api key, visit
+[https://my.pingpong.one/integrations/badge-uptime/](https://my.pingpong.one/integrations/badge-uptime/)
+`
+
+export const baseUrl = 'https://api.pingpong.one/widget/shields'
+
+export class BasePingPongService extends BaseJsonService {
+ static category = 'monitoring'
+
+ static validateApiKey({ apiKey }) {
+ if (!apiKey.startsWith('sp_')) {
+ throw new InvalidParameter({
+ prettyMessage: 'invalid api key',
+ })
+ }
+ }
+}
diff --git a/services/pingpong/pingpong-status.service.js b/services/pingpong/pingpong-status.service.js
new file mode 100644
index 0000000000000..1752820d17ea8
--- /dev/null
+++ b/services/pingpong/pingpong-status.service.js
@@ -0,0 +1,56 @@
+import Joi from 'joi'
+import { InvalidResponse, pathParams } from '../index.js'
+import { BasePingPongService, baseUrl, description } from './pingpong-base.js'
+
+const schema = Joi.object({
+ status: Joi.string().required(),
+}).required()
+
+export default class PingPongStatus extends BasePingPongService {
+ static route = { base: 'pingpong/status', pattern: ':apiKey' }
+
+ static openApi = {
+ '/pingpong/status/{apiKey}': {
+ get: {
+ summary: 'PingPong status',
+ description,
+ parameters: pathParams({
+ name: 'apiKey',
+ example: 'sp_2e80bc00b6054faeb2b87e2464be337e',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'status' }
+
+ static render({ status }) {
+ switch (status) {
+ case 'Operational':
+ return { message: 'up', color: 'brightgreen' }
+ case 'Major issues':
+ return { message: 'issues', color: 'orange' }
+ case 'Critical state':
+ return { message: 'down', color: 'red' }
+ case 'Maintenance mode':
+ return { message: 'maintenance', color: 'lightgrey' }
+ default:
+ throw new InvalidResponse({
+ prettyMessage: 'Unknown status received',
+ })
+ }
+ }
+
+ async fetch({ apiKey }) {
+ return this._requestJson({
+ schema,
+ url: `${baseUrl}/status/${apiKey}`,
+ })
+ }
+
+ async handle({ apiKey }) {
+ this.constructor.validateApiKey({ apiKey })
+ const { status } = await this.fetch({ apiKey, schema })
+ return this.constructor.render({ status })
+ }
+}
diff --git a/services/pingpong/pingpong-status.tester.js b/services/pingpong/pingpong-status.tester.js
new file mode 100644
index 0000000000000..fe147a63f9d76
--- /dev/null
+++ b/services/pingpong/pingpong-status.tester.js
@@ -0,0 +1,28 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+const isCorrectStatus = Joi.string().valid(
+ 'up',
+ 'issues',
+ 'down',
+ 'maintenance',
+)
+
+t.create('PingPong: Status (valid)')
+ .get('/sp_eb705b7c189f42e3b574dc790291c33f.json')
+ .expectBadge({ label: 'status', message: isCorrectStatus })
+
+t.create('PingPong: Status (valid, incorrect format)')
+ .get('/eb705b7c189f42e3b574dc790291c33f.json')
+ .expectBadge({ label: 'status', message: 'invalid api key' })
+
+t.create('PingPong: Status (unexpected response)')
+ .get('/sp_key.json')
+ .intercept(
+ nock =>
+ nock('https://api.pingpong.one')
+ .get('/widget/shields/status/sp_key')
+ .reply(200, '{"status": "up"}'), // unexpected status message
+ )
+ .expectBadge({ label: 'status', message: 'Unknown status received' })
diff --git a/services/pingpong/pingpong-uptime.service.js b/services/pingpong/pingpong-uptime.service.js
new file mode 100644
index 0000000000000..b3160fe035186
--- /dev/null
+++ b/services/pingpong/pingpong-uptime.service.js
@@ -0,0 +1,47 @@
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { pathParams } from '../index.js'
+import { BasePingPongService, baseUrl, description } from './pingpong-base.js'
+
+const schema = Joi.object({
+ uptime: Joi.number().min(0).max(100).required(),
+}).required()
+
+export default class PingPongUptime extends BasePingPongService {
+ static route = { base: 'pingpong/uptime', pattern: ':apiKey' }
+
+ static openApi = {
+ '/pingpong/uptime/{apiKey}': {
+ get: {
+ summary: 'PingPong uptime (last 30 days)',
+ description,
+ parameters: pathParams({
+ name: 'apiKey',
+ example: 'sp_2e80bc00b6054faeb2b87e2464be337e',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'uptime' }
+
+ static render({ uptime }) {
+ return {
+ message: `${uptime}%`,
+ color: coveragePercentage(uptime),
+ }
+ }
+
+ async fetch({ apiKey }) {
+ return this._requestJson({
+ schema,
+ url: `${baseUrl}/uptime/${apiKey}`,
+ })
+ }
+
+ async handle({ apiKey }) {
+ this.constructor.validateApiKey({ apiKey })
+ const { uptime } = await this.fetch({ apiKey, schema })
+ return this.constructor.render({ uptime })
+ }
+}
diff --git a/services/pingpong/pingpong-uptime.tester.js b/services/pingpong/pingpong-uptime.tester.js
new file mode 100644
index 0000000000000..0128983289ca4
--- /dev/null
+++ b/services/pingpong/pingpong-uptime.tester.js
@@ -0,0 +1,11 @@
+import { isPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('PingPong: Uptime (valid)')
+ .get('/sp_eb705b7c189f42e3b574dc790291c33f.json')
+ .expectBadge({ label: 'uptime', message: isPercentage })
+
+t.create('PingPong: Uptime (valid, incorrect format)')
+ .get('/eb705b7c189f42e3b574dc790291c33f.json')
+ .expectBadge({ label: 'uptime', message: 'invalid api key' })
diff --git a/services/pipenv-helpers.js b/services/pipenv-helpers.js
index c77b7babf2e7a..90c201c4278af 100644
--- a/services/pipenv-helpers.js
+++ b/services/pipenv-helpers.js
@@ -1,27 +1,50 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to pipenv
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
-const { InvalidParameter } = require('.')
+import Joi from 'joi'
+import { InvalidParameter } from './index.js'
-const isDependency = Joi.alternatives(
- Joi.object({
- version: Joi.string().required(),
- }).required(),
- Joi.object({
- ref: Joi.string().required(),
- }).required()
-)
+/**
+ * Joi schema for validating dependency.
+ *
+ * @type {Joi}
+ */
+const isDependency = Joi.object({
+ version: Joi.string(),
+ ref: Joi.string(),
+}).required()
+/**
+ * Joi schema for validating lock file object.
+ * Checks if the lock file object has required properties and the properties are valid.
+ *
+ * @type {Joi}
+ */
const isLockfile = Joi.object({
_meta: Joi.object({
requires: Joi.object({
python_version: Joi.string(),
}).required(),
}).required(),
- default: Joi.object().pattern(Joi.string().required(), isDependency),
- develop: Joi.object().pattern(Joi.string().required(), isDependency),
+ default: Joi.object().pattern(Joi.string(), isDependency),
+ develop: Joi.object().pattern(Joi.string(), isDependency),
}).required()
+/**
+ * Determines the dependency version based on the dependency type.
+ *
+ * @param {object} attrs - Refer to individual attributes
+ * @param {string} attrs.kind - Wanted dependency type ('dev' or 'default'), defaults to 'default'
+ * @param {string} attrs.wantedDependency - Name of the wanted dependency
+ * @param {object} attrs.lockfileData - Object containing lock file data
+ * @throws {Error} - Error if unknown dependency type provided
+ * @throws {InvalidParameter} - Error if wanted dependency is not present in lock file data
+ * @throws {InvalidParameter} - Error if version or ref is not present for the wanted dependency
+ * @returns {object} Object containing wanted dependency version or ref
+ */
function getDependencyVersion({
kind = 'default',
wantedDependency,
@@ -47,12 +70,17 @@ function getDependencyVersion({
if (version) {
// Strip the `==` which is always present.
return { version: version.replace('==', '') }
+ } else if (ref) {
+ if (ref.length === 40) {
+ // assume it is a commit hash
+ return { ref: ref.substring(0, 7) }
+ }
+ return { ref } // tag
} else {
- return { ref: ref.substring(1, 8) }
+ throw new InvalidParameter({
+ prettyMessage: `No version or ref for ${wantedDependency}`,
+ })
}
}
-module.exports = {
- isLockfile,
- getDependencyVersion,
-}
+export { isLockfile, getDependencyVersion }
diff --git a/services/pipenv-helpers.spec.js b/services/pipenv-helpers.spec.js
new file mode 100644
index 0000000000000..af7244ac64013
--- /dev/null
+++ b/services/pipenv-helpers.spec.js
@@ -0,0 +1,131 @@
+import { expect } from 'chai'
+import { getDependencyVersion } from './pipenv-helpers.js'
+import { InvalidParameter } from './index.js'
+
+describe('getDependencyVersion', function () {
+ // loosely based on https://github.com/pypa/pipfile#pipfilelock
+ const packages = {
+ chardet: {
+ hashes: [
+ 'sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691',
+ 'sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae',
+ ],
+ version: '==3.0.4',
+ },
+ django: {
+ editable: true,
+ git: 'https://github.com/django/django.git',
+ ref: '1.11.4001',
+ },
+ 'django-cms': {
+ file: 'https://github.com/divio/django-cms/archive/release/3.4.x.zip',
+ },
+ 'discordlists-py': {
+ git: 'https://github.com/HexCodeFFF/discordlists.py',
+ ref: '2df5a2b62144b49774728efa8267d909a8a9787f',
+ },
+ }
+
+ it('throws if dependency not found in (in default object with data)', function () {
+ expect(() =>
+ getDependencyVersion({
+ wantedDependency: 'requests',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ )
+ .to.throw(InvalidParameter)
+ .with.property('prettyMessage', 'default dependency not found')
+ })
+
+ it('throws if dependency not found in (in empty dev object)', function () {
+ expect(() =>
+ getDependencyVersion({
+ kind: 'dev',
+ wantedDependency: 'requests',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ )
+ .to.throw(InvalidParameter)
+ .with.property('prettyMessage', 'dev dependency not found')
+ })
+
+ it('tolerates missing keys', function () {
+ expect(
+ getDependencyVersion({
+ wantedDependency: 'chardet',
+ lockfileData: {
+ default: packages,
+ },
+ }),
+ ).to.deep.equal({ version: '3.0.4' })
+ })
+
+ it('finds package in develop object', function () {
+ expect(
+ getDependencyVersion({
+ kind: 'dev',
+ wantedDependency: 'chardet',
+ lockfileData: {
+ default: {},
+ develop: packages,
+ },
+ }),
+ ).to.deep.equal({ version: '3.0.4' })
+ })
+
+ it('returns version if present', function () {
+ expect(
+ getDependencyVersion({
+ wantedDependency: 'chardet',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ ).to.deep.equal({ version: '3.0.4' })
+ })
+
+ it('returns (complete) ref if ref is tag', function () {
+ expect(
+ getDependencyVersion({
+ wantedDependency: 'django',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ ).to.deep.equal({ ref: '1.11.4001' })
+ })
+
+ it('returns truncated ref if ref is commit hash', function () {
+ expect(
+ getDependencyVersion({
+ wantedDependency: 'discordlists-py',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ ).to.deep.equal({ ref: '2df5a2b' })
+ })
+
+ it('throws if no version or ref', function () {
+ expect(() =>
+ getDependencyVersion({
+ wantedDependency: 'django-cms',
+ lockfileData: {
+ default: packages,
+ develop: {},
+ },
+ }),
+ )
+ .to.throw(InvalidParameter)
+ .with.property('prettyMessage', 'No version or ref for django-cms')
+ })
+})
diff --git a/services/piwheels/piwheels-version.service.js b/services/piwheels/piwheels-version.service.js
new file mode 100644
index 0000000000000..49f9397fc014f
--- /dev/null
+++ b/services/piwheels/piwheels-version.service.js
@@ -0,0 +1,104 @@
+import Joi from 'joi'
+import {
+ BaseJsonService,
+ InvalidResponse,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { pep440VersionColor } from '../color-formatters.js'
+
+const schema = Joi.object({
+ releases: Joi.object()
+ .pattern(
+ Joi.string(),
+ Joi.object({
+ prerelease: Joi.boolean().required(),
+ yanked: Joi.boolean().required(),
+ files: Joi.object().required(),
+ }),
+ )
+ .required(),
+}).required()
+
+const queryParamSchema = Joi.object({
+ include_prereleases: Joi.equal(''),
+}).required()
+
+export default class PiWheelsVersion extends BaseJsonService {
+ static category = 'version'
+
+ static route = { base: 'piwheels/v', pattern: ':wheel', queryParamSchema }
+
+ static openApi = {
+ '/piwheels/v/{wheel}': {
+ get: {
+ summary: 'PiWheels Version',
+ description:
+ '[PiWheels](https://www.piwheels.org/) is a Python package repository providing Arm platform wheels for the Raspberry Pi',
+ parameters: [
+ pathParam({
+ name: 'wheel',
+ example: 'flask',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'piwheels' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version, versionFormatter: pep440VersionColor })
+ }
+
+ async fetch({ wheel }) {
+ return this._requestJson({
+ schema,
+ url: `https://www.piwheels.org/project/${wheel}/json/`,
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+
+ static transform(releases, includePrereleases) {
+ const allReleases = Object.keys(releases)
+ .reduce(
+ (acc, key) =>
+ acc.concat({
+ version: key,
+ prerelease: releases[key].prerelease,
+ yanked: releases[key].yanked,
+ hasFiles: Object.keys(releases[key].files).length > 0,
+ }),
+ [],
+ )
+ .filter(release => !release.yanked) // exclude any yanked releases
+ .filter(release => release.hasFiles) // exclude any releases with no wheels
+
+ if (allReleases.length === 0) {
+ throw new InvalidResponse({ prettyMessage: 'no versions found' })
+ }
+
+ if (includePrereleases) {
+ return allReleases[0].version
+ }
+
+ const stableReleases = allReleases.filter(release => !release.prerelease)
+ if (stableReleases.length > 0) {
+ return stableReleases[0].version
+ }
+ return allReleases[0].version
+ }
+
+ async handle({ wheel }, queryParams) {
+ const includePrereleases = queryParams.include_prereleases !== undefined
+ const { releases } = await this.fetch({ wheel })
+ const version = this.constructor.transform(releases, includePrereleases)
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/piwheels/piwheels-version.spec.js b/services/piwheels/piwheels-version.spec.js
new file mode 100644
index 0000000000000..70a9ee85c0373
--- /dev/null
+++ b/services/piwheels/piwheels-version.spec.js
@@ -0,0 +1,65 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse } from '../index.js'
+import PiWheelsVersion from './piwheels-version.service.js'
+
+describe('PiWheelsVersion', function () {
+ test(PiWheelsVersion.transform, () => {
+ given(
+ {
+ '2.0.0rc1': { prerelease: true, yanked: false, files: { foobar: {} } },
+ '1.9.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ },
+ false,
+ ).expect('1.9.0')
+ given(
+ {
+ '2.0.0rc1': { prerelease: true, yanked: false, files: { foobar: {} } },
+ '1.9.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ },
+ true,
+ ).expect('2.0.0rc1')
+ given(
+ {
+ '2.0.0': { prerelease: false, yanked: true, files: { foobar: {} } },
+ '1.9.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ },
+ false,
+ ).expect('1.9.0')
+ given(
+ {
+ '2.0.0': { prerelease: false, yanked: false, files: {} },
+ '1.9.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ },
+ false,
+ ).expect('1.9.0')
+ given(
+ {
+ '2.0.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ '1.9.0': { prerelease: false, yanked: false, files: { foobar: {} } },
+ },
+ false,
+ ).expect('2.0.0')
+ given(
+ {
+ '2.0.0rc2': { prerelease: true, yanked: false, files: { foobar: {} } },
+ '2.0.0rc1': { prerelease: true, yanked: false, files: { foobar: {} } },
+ },
+ false,
+ ).expect('2.0.0rc2')
+ })
+
+ it('throws `no releases` InvalidResponse if no versions', function () {
+ expect(() =>
+ PiWheelsVersion.transform(
+ {
+ '1.0.1': { prerelease: false, yanked: false, files: {} },
+ '1.0.0': { prerelease: false, yanked: true, files: { foobar: {} } },
+ },
+ false,
+ ),
+ )
+ .to.throw(InvalidResponse)
+ .with.property('prettyMessage', 'no versions found')
+ })
+})
diff --git a/services/piwheels/piwheels-version.tester.js b/services/piwheels/piwheels-version.tester.js
new file mode 100644
index 0000000000000..674596b1c0c49
--- /dev/null
+++ b/services/piwheels/piwheels-version.tester.js
@@ -0,0 +1,13 @@
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('version (valid)').get('/flask.json').expectBadge({
+ label: 'piwheels',
+ message: isVPlusDottedVersionNClauses,
+})
+
+t.create('version (does not exist)').get('/doesn-not-exist.json').expectBadge({
+ label: 'piwheels',
+ message: 'package not found',
+})
diff --git a/services/pkgreview/package-rating.service.js b/services/pkgreview/package-rating.service.js
deleted file mode 100644
index 39b0fb9f1f6c1..0000000000000
--- a/services/pkgreview/package-rating.service.js
+++ /dev/null
@@ -1,94 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { starRating, metric } = require('../text-formatters')
-const { colorScale } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const pkgReviewColor = colorScale([2, 3, 4])
-
-const schema = Joi.object({
- rating: Joi.number()
- .min(0)
- .max(1)
- .precision(1)
- .required()
- .allow(null),
- reviewsCount: nonNegativeInteger,
-}).required()
-
-module.exports = class PkgreviewRating extends BaseJsonService {
- static get category() {
- return 'rating'
- }
-
- static get route() {
- return {
- base: 'pkgreview',
- pattern: ':format(rating|stars)/:pkgManager(npm)/:pkgSlug+',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'pkgreview.dev Package Ratings',
- pattern: 'rating/:pkgManager/:pkgSlug+',
- namedParams: { pkgManager: 'npm', pkgSlug: 'react' },
- staticPreview: this.render({
- format: 'rating',
- rating: 3.5,
- reviewsCount: 237,
- }),
- },
- {
- title: 'pkgreview.dev Star Ratings',
- pattern: 'stars/:pkgManager/:pkgSlug+',
- namedParams: { pkgManager: 'npm', pkgSlug: 'react' },
- staticPreview: this.render({
- format: 'stars',
- rating: 1.5,
- reviewsCount: 200,
- }),
- },
- ]
- }
-
- static render({ rating, reviewsCount, format }) {
- const message =
- format === 'rating'
- ? `${+parseFloat(rating).toFixed(1)}/5 (${metric(reviewsCount)})`
- : starRating(rating)
-
- return {
- message,
- label: format,
- color: pkgReviewColor(rating),
- }
- }
-
- async fetch({ pkgManager, pkgSlug }) {
- return this._requestJson({
- schema,
- url: `https://pkgreview.dev/api/v1/${pkgManager}/${encodeURIComponent(
- pkgSlug
- )}`,
- errorMessages: {
- 404: 'package not found',
- },
- })
- }
-
- async handle({ format, pkgManager, pkgSlug }) {
- const { reviewsCount, rating } = await this.fetch({
- pkgManager,
- pkgSlug,
- })
- return this.constructor.render({
- reviewsCount,
- format,
- rating: rating * 5,
- })
- }
-}
diff --git a/services/pkgreview/package-rating.tester.js b/services/pkgreview/package-rating.tester.js
deleted file mode 100644
index a9859265224aa..0000000000000
--- a/services/pkgreview/package-rating.tester.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const { withRegex, isStarRating } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isRatingWithReviews = withRegex(
- /^(([0-4](.?([0-9]))?)|5)\/5?\s*\([0-9]*\)$/
-)
-
-t.create('Stars Badge renders')
- .get('/stars/npm/react.json')
- .expectBadge({ label: 'stars', message: isStarRating })
-
-t.create('Rating Badge renders')
- .get('/rating/npm/react.json')
- .expectBadge({ label: 'rating', message: isRatingWithReviews })
-
-t.create('nonexistent package')
- .get('/rating/npm/ohlolweallknowthispackagewontexist.json')
- .expectBadge({
- label: 'rating',
- message: 'package not found',
- color: 'red',
- })
diff --git a/services/poeditor/poeditor.service.js b/services/poeditor/poeditor.service.js
index e5bc71d794c30..b040168596a57 100644
--- a/services/poeditor/poeditor.service.js
+++ b/services/poeditor/poeditor.service.js
@@ -1,19 +1,19 @@
-'use strict'
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { coveragePercentage } from '../color-formatters.js'
+import {
+ BaseJsonService,
+ InvalidResponse,
+ pathParam,
+ queryParam,
+} from '../index.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { coveragePercentage } = require('../color-formatters')
-const { BaseJsonService, InvalidResponse } = require('..')
+const description = `
+POEditor is an web-based tool for translation and internationalization
-const documentation = `
-
- You must specify the read-only API token from the POEditor account to which the project belongs.
-
-
- As per the POEditor API documentation ,
- all requests to the API must contain the parameter api_token. You can get a read-only key from your POEditor account.
- You'll find it in My Account > API Access .
-
+All requests to must contain the parameter \`token\`.
+You can get a read-only token from your POEditor account in [My Account > API Access](https://poeditor.com/account/api).
+This token will be exposed as part of the badge URL so be sure to generate a read-only token.
`
const schema = Joi.object({
@@ -26,10 +26,7 @@ const schema = Joi.object({
.items({
name: Joi.string().required(),
code: Joi.string().required(),
- percentage: Joi.number()
- .min(0)
- .max(100)
- .required(),
+ percentage: Joi.number().min(0).max(100).required(),
})
.required(),
}),
@@ -39,34 +36,33 @@ const queryParamSchema = Joi.object({
token: Joi.string().required(),
}).required()
-module.exports = class POEditor extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class POEditor extends BaseJsonService {
+ static category = 'other'
- static get route() {
- return {
- base: 'poeditor',
- pattern: 'progress/:projectId/:languageCode',
- queryParamSchema,
- }
+ static route = {
+ base: 'poeditor',
+ pattern: 'progress/:projectId/:languageCode',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'POEditor',
- namedParams: { projectId: '323337', languageCode: 'fr' },
- queryParams: { token: 'abc123def456' },
- staticPreview: this.render({
- code: 200,
- message: 'OK',
- language: { percentage: 93, code: 'fr', name: 'French' },
- }),
- keywords: ['l10n'],
- documentation,
+ static openApi = {
+ '/poeditor/progress/{projectId}/{languageCode}': {
+ get: {
+ summary: 'POEditor',
+ description,
+ parameters: [
+ pathParam({ name: 'projectId', example: '323337' }),
+ pathParam({ name: 'languageCode', example: 'fr' }),
+ queryParam({
+ name: 'token',
+ example: 'abc123def456',
+ description:
+ 'A read-only token from your POEditor account from [My Account > API Access](https://poeditor.com/account/api)',
+ required: true,
+ }),
+ ],
},
- ]
+ },
}
static render({ code, message, language }) {
diff --git a/services/poeditor/poeditor.tester.js b/services/poeditor/poeditor.tester.js
index 78db3b1657369..1797ae78acda0 100644
--- a/services/poeditor/poeditor.tester.js
+++ b/services/poeditor/poeditor.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('gets POEditor progress online')
.get('/progress/323337/de.json?token=7a666b44c0985d16a7b59748f488275c')
@@ -52,7 +51,7 @@ t.create('gets mock POEditor progress')
id: '1234',
api_token: 'abc123def456',
})
- .reply(200, apiResponse)
+ .reply(200, apiResponse),
)
.expectBadge({
label: 'French',
@@ -67,7 +66,7 @@ t.create('handles requests for missing languages')
id: '1234',
api_token: 'abc123def456',
})
- .reply(200, apiResponse)
+ .reply(200, apiResponse),
)
.expectBadge({
label: 'other',
@@ -88,7 +87,7 @@ t.create('handles requests for wrong keys')
code: '403',
message: "You don't have permission to access this resource",
},
- })
+ }),
)
.expectBadge({
label: 'other',
diff --git a/services/polymart/polymart-downloads.service.js b/services/polymart/polymart-downloads.service.js
new file mode 100644
index 0000000000000..dbfbee76a8d68
--- /dev/null
+++ b/services/polymart/polymart-downloads.service.js
@@ -0,0 +1,11 @@
+import redirector from '../../core/base-service/redirector.js'
+
+export default redirector({
+ category: 'downloads',
+ route: {
+ base: 'polymart/downloads',
+ pattern: ':resourceId',
+ },
+ transformPath: ({ resourceId }) => `/voxel-shop/dt/${resourceId}`,
+ dateAdded: new Date('2026-04-05'),
+})
diff --git a/services/polymart/polymart-downloads.tester.js b/services/polymart/polymart-downloads.tester.js
new file mode 100644
index 0000000000000..5c6beab15709e
--- /dev/null
+++ b/services/polymart/polymart-downloads.tester.js
@@ -0,0 +1,6 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('polymart downloads redirect')
+ .get('/323.svg')
+ .expectRedirect('/voxel-shop/dt/323.svg')
diff --git a/services/polymart/polymart-latest-version.service.js b/services/polymart/polymart-latest-version.service.js
new file mode 100644
index 0000000000000..bd649bb9ac50b
--- /dev/null
+++ b/services/polymart/polymart-latest-version.service.js
@@ -0,0 +1,11 @@
+import redirector from '../../core/base-service/redirector.js'
+
+export default redirector({
+ category: 'version',
+ route: {
+ base: 'polymart/version',
+ pattern: ':resourceId',
+ },
+ transformPath: ({ resourceId }) => `/voxel-shop/v/${resourceId}`,
+ dateAdded: new Date('2026-04-05'),
+})
diff --git a/services/polymart/polymart-latest-version.tester.js b/services/polymart/polymart-latest-version.tester.js
new file mode 100644
index 0000000000000..4343b793eea2b
--- /dev/null
+++ b/services/polymart/polymart-latest-version.tester.js
@@ -0,0 +1,6 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('polymart version redirect')
+ .get('/323.svg')
+ .expectRedirect('/voxel-shop/v/323.svg')
diff --git a/services/polymart/polymart-rating.service.js b/services/polymart/polymart-rating.service.js
new file mode 100644
index 0000000000000..c2601e371cacc
--- /dev/null
+++ b/services/polymart/polymart-rating.service.js
@@ -0,0 +1,15 @@
+import redirector from '../../core/base-service/redirector.js'
+
+export default [
+ redirector({
+ category: 'rating',
+ name: 'PolymartRatingRedirect',
+ route: {
+ base: 'polymart',
+ pattern: ':format(rating|stars)/:resourceId',
+ },
+ transformPath: ({ format, resourceId }) =>
+ `/voxel-shop/${format}/${resourceId}`,
+ dateAdded: new Date('2026-04-05'),
+ }),
+]
diff --git a/services/polymart/polymart-rating.tester.js b/services/polymart/polymart-rating.tester.js
new file mode 100644
index 0000000000000..9ff4ccabfee63
--- /dev/null
+++ b/services/polymart/polymart-rating.tester.js
@@ -0,0 +1,15 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'PolymartRatingRedirect',
+ title: 'PolymartRatingRedirect',
+ pathPrefix: '/polymart',
+})
+
+t.create('polymart rating redirect')
+ .get('/rating/323.svg')
+ .expectRedirect('/voxel-shop/rating/323.svg')
+
+t.create('polymart stars redirect')
+ .get('/stars/323.svg')
+ .expectRedirect('/voxel-shop/stars/323.svg')
diff --git a/services/powershellgallery/powershellgallery.service.js b/services/powershellgallery/powershellgallery.service.js
index 9eef674082ed1..24fc16780cc17 100644
--- a/services/powershellgallery/powershellgallery.service.js
+++ b/services/powershellgallery/powershellgallery.service.js
@@ -1,10 +1,5 @@
-'use strict'
-
-const {
- fetch,
- createServiceFamily,
-} = require('../nuget/nuget-v2-service-family')
-const { BaseXmlService } = require('..')
+import { fetch, createServiceFamily } from '../nuget/nuget-v2-service-family.js'
+import { BaseXmlService, pathParams } from '../index.js'
const WINDOWS_TAG_NAME = 'windows'
const MACOS_TAG_NAME = 'macos'
@@ -21,42 +16,32 @@ const {
defaultLabel: 'powershell gallery',
serviceBaseUrl: 'powershellgallery',
apiBaseUrl,
- odataFormat: 'xml',
title: 'PowerShell Gallery',
examplePackageName: 'Azure.Storage',
- exampleVersion: '4.4.0',
- examplePrereleaseVersion: '4.4.1-preview',
- exampleDownloadCount: 1.2e7,
})
class PowershellGalleryPlatformSupport extends BaseXmlService {
- static get category() {
- return 'platform-support'
- }
+ static category = 'platform-support'
- static get route() {
- return {
- base: 'powershellgallery/p',
- pattern: ':packageName',
- }
+ static route = {
+ base: 'powershellgallery/p',
+ pattern: ':packageName',
}
- static get examples() {
- return [
- {
- title: 'PowerShell Gallery',
- namedParams: { packageName: 'DNS.1.1.1.1' },
- staticPreview: this.render({
- platforms: ['windows', 'macos', 'linux'],
+ static openApi = {
+ '/powershellgallery/p/{packageName}': {
+ get: {
+ summary: 'PowerShell Gallery Platform Support',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'PackageManagement',
}),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'platform',
- }
+ static defaultBadgeData = {
+ label: 'platform',
}
static render({ platforms }) {
@@ -68,7 +53,6 @@ class PowershellGalleryPlatformSupport extends BaseXmlService {
async handle({ packageName }) {
const { Tags: tagStr } = await fetch(this, {
baseUrl: apiBaseUrl,
- odataFormat: 'xml',
packageName,
})
@@ -105,7 +89,7 @@ class PowershellGalleryPlatformSupport extends BaseXmlService {
}
}
-module.exports = {
+export {
PowershellGalleryVersion,
PowershellGalleryVersionRedirector,
PowershellGalleryDownloads,
diff --git a/services/powershellgallery/powershellgallery.tester.js b/services/powershellgallery/powershellgallery.tester.js
index 778d9fd487c11..61b7a95d70dde 100644
--- a/services/powershellgallery/powershellgallery.tester.js
+++ b/services/powershellgallery/powershellgallery.tester.js
@@ -1,39 +1,32 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const {
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
isMetric,
isVPlusDottedVersionNClauses,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
+} from '../test-validators.js'
const isPlatform = Joi.string().regex(
- /^(windows|linux|macos)( \| (windows|linux|macos))*$/
+ /^(windows|linux|macos)( \| (windows|linux|macos))*$/,
)
-const t = new ServiceTester({
+export const t = new ServiceTester({
id: 'powershellgallery',
title: 'PowerShell Gallery',
})
-module.exports = t
-t.create('total downloads (valid)')
- .get('/dt/ACMESharp.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('total downloads (valid)').get('/dt/ACMESharp.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('total downloads (not found)')
.get('/dt/not-a-real-package.json')
.expectBadge({ label: 'downloads', message: 'not found' })
-t.create('version (valid)')
- .get('/v/ACMESharp.json')
- .expectBadge({
- label: 'powershell gallery',
- message: isVPlusDottedVersionNClauses,
- })
+t.create('version (valid)').get('/v/ACMESharp.json').expectBadge({
+ label: 'powershell gallery',
+ message: isVPlusDottedVersionNClauses,
+})
t.create('version (not found)')
.get('/v/not-a-real-package.json')
@@ -51,16 +44,17 @@ t.create('version (pre) (not found)')
.expectBadge({ label: 'powershell gallery', message: 'not found' })
t.create('version (legacy redirect: vpre)')
- .get('/vpre/ACMESharp.svg')
- .expectRedirect('/powershellgallery/v/ACMESharp.svg?include_prereleases')
-
-t.create('platform (valid')
- .get('/p/DNS.1.1.1.1.json')
+ .get('/vpre/ACMESharp.json')
.expectBadge({
- label: 'platform',
- message: isPlatform,
+ label: 'powershell gallery',
+ message: 'https://github.com/badges/shields/pull/11583',
})
+t.create('platform (valid)').get('/p/PackageManagement.json').expectBadge({
+ label: 'platform',
+ message: isPlatform,
+})
+
t.create('platform (no tags)')
.get('/p/ACMESharp.json')
.expectBadge({ label: 'platform', message: 'not specified' })
diff --git a/services/pub/pub-common.js b/services/pub/pub-common.js
new file mode 100644
index 0000000000000..a93d10bed5c52
--- /dev/null
+++ b/services/pub/pub-common.js
@@ -0,0 +1,4 @@
+const baseDescription =
+ 'Pub is a package registry for Dart and Flutter.
'
+
+export { baseDescription }
diff --git a/services/pub/pub-downloads.service.js b/services/pub/pub-downloads.service.js
new file mode 100644
index 0000000000000..eb79c0558e8ca
--- /dev/null
+++ b/services/pub/pub-downloads.service.js
@@ -0,0 +1,53 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { baseDescription } from './pub-common.js'
+
+const description = `${baseDescription}
+ This badge shows a measure of how many developers have downloaded a package monthly.
`
+
+const schema = Joi.object({
+ downloadCount30Days: nonNegativeInteger,
+}).required()
+
+export default class PubDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = { base: 'pub/dm', pattern: ':packageName' }
+
+ static openApi = {
+ '/pub/dm/{packageName}': {
+ get: {
+ summary: 'Pub Monthly Downloads',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'analysis_options',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ static render({ downloadCount30Days }) {
+ return renderDownloadsBadge({
+ downloads: downloadCount30Days,
+ interval: 'month',
+ })
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://pub.dev/api/packages/${packageName}/score`,
+ })
+ }
+
+ async handle({ packageName }) {
+ const score = await this.fetch({ packageName })
+ const downloadCount30Days = score.downloadCount30Days
+ return this.constructor.render({ downloadCount30Days })
+ }
+}
diff --git a/services/pub/pub-downloads.tester.js b/services/pub/pub-downloads.tester.js
new file mode 100644
index 0000000000000..573393f75ea84
--- /dev/null
+++ b/services/pub/pub-downloads.tester.js
@@ -0,0 +1,20 @@
+import { isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('pub monthly downloads (valid)')
+ .get('/analysis_options.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+ color: 'green',
+ })
+
+t.create('pub monthly downloads (not found)')
+ .get('/analysisoptions.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'not found',
+ color: 'red',
+ })
diff --git a/services/pub/pub-likes.service.js b/services/pub/pub-likes.service.js
new file mode 100644
index 0000000000000..33137bf5e38cb
--- /dev/null
+++ b/services/pub/pub-likes.service.js
@@ -0,0 +1,54 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { baseDescription } from './pub-common.js'
+
+const description = `${baseDescription}
+ This badge shows a measure of how many developers have liked a package. This provides a raw measure of the overall sentiment of a package from peer developers.
`
+
+const schema = Joi.object({
+ likeCount: nonNegativeInteger,
+}).required()
+
+export default class PubLikes extends BaseJsonService {
+ static category = 'rating'
+
+ static route = { base: 'pub/likes', pattern: ':packageName' }
+
+ static openApi = {
+ '/pub/likes/{packageName}': {
+ get: {
+ summary: 'Pub Likes',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'analysis_options',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'likes' }
+
+ static render({ likeCount }) {
+ return {
+ label: 'likes',
+ message: metric(likeCount),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://pub.dev/api/packages/${packageName}/score`,
+ })
+ }
+
+ async handle({ packageName }) {
+ const score = await this.fetch({ packageName })
+ const likeCount = score.likeCount
+ return this.constructor.render({ likeCount })
+ }
+}
diff --git a/services/pub/pub-likes.tester.js b/services/pub/pub-likes.tester.js
new file mode 100644
index 0000000000000..7bbe1d36af469
--- /dev/null
+++ b/services/pub/pub-likes.tester.js
@@ -0,0 +1,16 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('pub likes (valid)').get('/analysis_options.json').expectBadge({
+ label: 'likes',
+ message: isMetric,
+ color: 'blue',
+})
+
+t.create('pub likes (not found)').get('/analysisoptions.json').expectBadge({
+ label: 'likes',
+ message: 'not found',
+ color: 'red',
+})
diff --git a/services/pub/pub-points.service.js b/services/pub/pub-points.service.js
new file mode 100644
index 0000000000000..177d26868e479
--- /dev/null
+++ b/services/pub/pub-points.service.js
@@ -0,0 +1,56 @@
+import Joi from 'joi'
+import { floorCount } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { baseDescription } from './pub-common.js'
+
+const description = `${baseDescription}
+ This badge shows a measure of quality. This includes several dimensions of quality such as code style, platform support, and maintainability.
`
+
+const schema = Joi.object({
+ grantedPoints: nonNegativeInteger,
+ maxPoints: nonNegativeInteger,
+}).required()
+
+export default class PubPoints extends BaseJsonService {
+ static category = 'rating'
+
+ static route = { base: 'pub/points', pattern: ':packageName' }
+
+ static openApi = {
+ '/pub/points/{packageName}': {
+ get: {
+ summary: 'Pub Points',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'analysis_options',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'points' }
+
+ static render({ grantedPoints, maxPoints }) {
+ return {
+ label: 'points',
+ message: `${grantedPoints}/${maxPoints}`,
+ color: floorCount((grantedPoints / maxPoints) * 100, 40, 60, 80),
+ }
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://pub.dev/api/packages/${packageName}/score`,
+ })
+ }
+
+ async handle({ packageName }) {
+ const score = await this.fetch({ packageName })
+ const grantedPoints = score.grantedPoints
+ const maxPoints = score.maxPoints
+ return this.constructor.render({ grantedPoints, maxPoints })
+ }
+}
diff --git a/services/pub/pub-points.tester.js b/services/pub/pub-points.tester.js
new file mode 100644
index 0000000000000..61b27a3e869be
--- /dev/null
+++ b/services/pub/pub-points.tester.js
@@ -0,0 +1,17 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('pub points (valid)')
+ .get('/analysis_options.json')
+ .expectBadge({
+ label: 'points',
+ message: Joi.string().regex(/^\d+\/\d+$/),
+ })
+
+t.create('pub points (not found)').get('/analysisoptions.json').expectBadge({
+ label: 'points',
+ message: 'not found',
+ color: 'red',
+})
diff --git a/services/pub/pub-publisher.service.js b/services/pub/pub-publisher.service.js
new file mode 100644
index 0000000000000..d1e821b9e1482
--- /dev/null
+++ b/services/pub/pub-publisher.service.js
@@ -0,0 +1,54 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { baseDescription } from './pub-common.js'
+
+const schema = Joi.object({
+ publisherId: Joi.string().allow(null).required(),
+}).required()
+
+export class PubPublisher extends BaseJsonService {
+ static category = 'other'
+
+ static route = {
+ base: 'pub/publisher',
+ pattern: ':packageName',
+ }
+
+ static openApi = {
+ '/pub/publisher/{packageName}': {
+ get: {
+ summary: 'Pub Publisher',
+ description: baseDescription,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'path',
+ }),
+ },
+ },
+ }
+
+ static _cacheLength = 3600
+
+ static defaultBadgeData = { label: 'publisher' }
+
+ static render({ publisher }) {
+ return {
+ label: 'publisher',
+ message: publisher == null ? 'unverified' : publisher,
+ color: publisher == null ? 'lightgrey' : 'blue',
+ }
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://pub.dev/api/packages/${packageName}/publisher`,
+ })
+ }
+
+ async handle({ packageName }) {
+ const data = await this.fetch({ packageName })
+ const publisher = data.publisherId
+ return this.constructor.render({ publisher })
+ }
+}
diff --git a/services/pub/pub-publisher.tester.js b/services/pub/pub-publisher.tester.js
new file mode 100644
index 0000000000000..d74d46d4b5962
--- /dev/null
+++ b/services/pub/pub-publisher.tester.js
@@ -0,0 +1,18 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('package publisher').get('/path.json').expectBadge({
+ label: 'publisher',
+ message: 'dart.dev',
+})
+
+t.create('package not verified publisher').get('/utf.json').expectBadge({
+ label: 'publisher',
+ message: 'unverified',
+ color: 'lightgrey',
+})
+
+t.create('package not found').get('/doesnotexist.json').expectBadge({
+ label: 'publisher',
+ message: 'not found',
+})
diff --git a/services/pub/pub.service.js b/services/pub/pub.service.js
index f68e4a97605ac..696a6afb23dd5 100644
--- a/services/pub/pub.service.js
+++ b/services/pub/pub.service.js
@@ -1,12 +1,16 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { latest, renderVersionBadge } = require('../version')
-const { BaseJsonService, redirector } = require('..')
+import Joi from 'joi'
+import { latest, renderVersionBadge } from '../version.js'
+import {
+ BaseJsonService,
+ retiredService,
+ pathParam,
+ queryParam,
+} from '../index.js'
+import { baseDescription } from './pub-common.js'
const schema = Joi.object({
versions: Joi.array()
- .items(Joi.string())
+ .items(Joi.object({ version: Joi.string().required() }))
.required(),
}).required()
@@ -15,67 +19,61 @@ const queryParamSchema = Joi.object({
}).required()
class PubVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: 'pub/v',
- pattern: ':packageName',
- queryParamSchema,
- }
+ static route = {
+ base: 'pub/v',
+ pattern: ':packageName',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Pub Version',
- namedParams: { packageName: 'box2d' },
- staticPreview: renderVersionBadge({ version: 'v0.4.0' }),
- keywords: ['dart', 'dartlang'],
+ static openApi = {
+ '/pub/v/{packageName}': {
+ get: {
+ summary: 'Pub Version',
+ description: baseDescription,
+ parameters: [
+ pathParam({
+ name: 'packageName',
+ example: 'box2d',
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
},
- {
- title: 'Pub Version (including pre-releases)',
- namedParams: { packageName: 'box2d' },
- queryParams: { include_prereleases: null },
- staticPreview: renderVersionBadge({ version: 'v0.4.0' }),
- keywords: ['dart', 'dartlang'],
- },
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'pub' }
- }
+ static defaultBadgeData = { label: 'pub' }
async fetch({ packageName }) {
return this._requestJson({
schema,
- url: `https://pub.dartlang.org/packages/${packageName}.json`,
+ url: `https://pub.dev/api/packages/${packageName}`,
})
}
async handle({ packageName }, queryParams) {
const data = await this.fetch({ packageName })
const includePre = queryParams.include_prereleases !== undefined
- const versions = data.versions
+ const versions = data.versions.map(x => x.version)
const version = latest(versions, { pre: includePre })
return renderVersionBadge({ version })
}
}
-const PubVersionRedirector = redirector({
+const PubVersionRedirector = retiredService({
category: 'version',
+ label: 'pub',
route: {
base: 'pub/vpre',
pattern: ':packageName',
},
- transformPath: ({ packageName }) => `/pub/v/${packageName}`,
- transformQueryParams: params => ({
- include_prereleases: null,
- }),
- dateAdded: new Date('2019-12-15'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
})
-module.exports = { PubVersion, PubVersionRedirector }
+export { PubVersion, PubVersionRedirector }
diff --git a/services/pub/pub.tester.js b/services/pub/pub.tester.js
index 74c9db3b203ce..996795cb3b5e3 100644
--- a/services/pub/pub.tester.js
+++ b/services/pub/pub.tester.js
@@ -1,19 +1,15 @@
-'use strict'
-
-const { isVPlusTripleDottedVersion } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import { isVPlusTripleDottedVersion } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'PubVersion',
title: 'Pub Version',
pathPrefix: '/pub',
-}))
+})
-t.create('package version')
- .get('/v/box2d.json')
- .expectBadge({
- label: 'pub',
- message: isVPlusTripleDottedVersion,
- })
+t.create('package version').get('/v/box2d.json').expectBadge({
+ label: 'pub',
+ message: isVPlusTripleDottedVersion,
+})
t.create('package pre-release version')
.get('/v/box2d.json?include_prereleases')
@@ -22,13 +18,14 @@ t.create('package pre-release version')
message: isVPlusTripleDottedVersion,
})
-t.create('package not found')
- .get('/v/does-not-exist.json')
+t.create('package not found').get('/v/doesnotexist.json').expectBadge({
+ label: 'pub',
+ message: 'not found',
+})
+
+t.create('package version (legacy redirect: vpre)')
+ .get('/vpre/box2d.json')
.expectBadge({
label: 'pub',
- message: 'not found',
+ message: 'https://github.com/badges/shields/pull/11583',
})
-
-t.create('package version (legacy redirect: vpre)')
- .get('/vpre/box2d.svg')
- .expectRedirect('/pub/v/box2d.svg?include_prereleases')
diff --git a/services/pulsar/pulsar-downloads.service.js b/services/pulsar/pulsar-downloads.service.js
new file mode 100644
index 0000000000000..aa4e3d870a982
--- /dev/null
+++ b/services/pulsar/pulsar-downloads.service.js
@@ -0,0 +1,51 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { pulsarPurple } from './pulsar-helper.js'
+
+const schema = Joi.object({
+ downloads: nonNegativeInteger,
+})
+
+export default class PulsarDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = { base: 'pulsar/dt', pattern: ':packageName' }
+
+ static openApi = {
+ '/pulsar/dt/{packageName}': {
+ get: {
+ summary: 'Pulsar Downloads',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'hey-pane',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ static render({ downloadCount }) {
+ return {
+ label: 'downloads',
+ message: metric(downloadCount),
+ color: pulsarPurple,
+ }
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://api.pulsar-edit.dev/api/packages/${packageName}`,
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+
+ async handle({ packageName }) {
+ const packageData = await this.fetch({ packageName })
+ const downloadCount = packageData.downloads
+ return this.constructor.render({ downloadCount })
+ }
+}
diff --git a/services/pulsar/pulsar-downloads.tester.js b/services/pulsar/pulsar-downloads.tester.js
new file mode 100644
index 0000000000000..1758516a12fae
--- /dev/null
+++ b/services/pulsar/pulsar-downloads.tester.js
@@ -0,0 +1,18 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import { pulsarPurple } from './pulsar-helper.js'
+
+export const t = await createServiceTester()
+
+t.create('pulsar downloads (valid)')
+ .get('/hey-pane.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetric,
+ color: `#${pulsarPurple}`,
+ })
+
+t.create('pulsar downloads (not found)').get('/test-package.json').expectBadge({
+ label: 'downloads',
+ message: 'package not found',
+})
diff --git a/services/pulsar/pulsar-helper.js b/services/pulsar/pulsar-helper.js
new file mode 100644
index 0000000000000..42e437622cd21
--- /dev/null
+++ b/services/pulsar/pulsar-helper.js
@@ -0,0 +1,7 @@
+// This is based on the format the Docker badges have taken.
+// Seems Tests require `#` before colors, whereas the badges do not.
+// So a color variable can be exported for all modules to use as needed.
+
+const pulsarPurple = '662d91'
+
+export { pulsarPurple }
diff --git a/services/pulsar/pulsar-stargazers.service.js b/services/pulsar/pulsar-stargazers.service.js
new file mode 100644
index 0000000000000..eab81ebeca5c5
--- /dev/null
+++ b/services/pulsar/pulsar-stargazers.service.js
@@ -0,0 +1,51 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { pulsarPurple } from './pulsar-helper.js'
+
+const schema = Joi.object({
+ stargazers_count: nonNegativeInteger,
+})
+
+export default class PulsarStargazers extends BaseJsonService {
+ static category = 'rating'
+
+ static route = { base: 'pulsar/stargazers', pattern: ':packageName' }
+
+ static openApi = {
+ '/pulsar/stargazers/{packageName}': {
+ get: {
+ summary: 'Pulsar Stargazers',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'hey-pane',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'stargazers' }
+
+ static render({ stargazerCount }) {
+ return {
+ label: 'stargazers',
+ message: metric(stargazerCount),
+ color: pulsarPurple,
+ }
+ }
+
+ async fetch({ packageName }) {
+ return this._requestJson({
+ schema,
+ url: `https://api.pulsar-edit.dev/api/packages/${packageName}`,
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+
+ async handle({ packageName }) {
+ const packageData = await this.fetch({ packageName })
+ const stargazerCount = packageData.stargazers_count
+ return this.constructor.render({ stargazerCount })
+ }
+}
diff --git a/services/pulsar/pulsar-stargazers.tester.js b/services/pulsar/pulsar-stargazers.tester.js
new file mode 100644
index 0000000000000..3207c4ebd2184
--- /dev/null
+++ b/services/pulsar/pulsar-stargazers.tester.js
@@ -0,0 +1,20 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import { pulsarPurple } from './pulsar-helper.js'
+
+export const t = await createServiceTester()
+
+t.create('pulsar stargazers (valid)')
+ .get('/hey-pane.json')
+ .expectBadge({
+ label: 'stargazers',
+ message: isMetric,
+ color: `#${pulsarPurple}`,
+ })
+
+t.create('pulsar stargazers (not found)')
+ .get('/test-package.json')
+ .expectBadge({
+ label: 'stargazers',
+ message: 'package not found',
+ })
diff --git a/services/puppetforge/puppetforge-base.js b/services/puppetforge/puppetforge-base.js
index db48d825d10d8..b277b43a5ec78 100644
--- a/services/puppetforge/puppetforge-base.js
+++ b/services/puppetforge/puppetforge-base.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger, semver } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { nonNegativeInteger, semver } from '../validators.js'
+import { BaseJsonService } from '../index.js'
const usersSchema = Joi.object({
module_count: nonNegativeInteger,
@@ -11,28 +9,42 @@ const usersSchema = Joi.object({
const modulesSchema = Joi.object({
endorsement: Joi.string().allow(null),
- feedback_score: Joi.number()
- .integer()
- .min(0)
- .allow(null),
+ feedback_score: Joi.number().integer().min(0).allow(null),
downloads: nonNegativeInteger,
current_release: Joi.alternatives(
Joi.object({
- pdk: Joi.boolean()
- .valid(true)
- .required(),
+ pdk: Joi.boolean().valid(true).required(),
version: semver,
metadata: Joi.object({ 'pdk-version': semver }).required(),
}).required(),
Joi.object({
- pdk: Joi.boolean()
- .valid(false)
- .required(),
+ pdk: Joi.boolean().valid(false).required(),
version: semver,
- }).required()
+ }).required(),
),
}).required()
+const modulesValidationSchema = Joi.array()
+ .items(
+ Joi.alternatives().try(
+ Joi.object({
+ name: Joi.string().valid('total').required(),
+ score: nonNegativeInteger,
+ }).required(),
+ Joi.object({}).required(),
+ ),
+ )
+ .custom((value, helpers) => {
+ // Custom validation to check for exactly one type1 object
+ const totalCount = value.filter(item => item.name === 'total').length
+ if (totalCount !== 1) {
+ return helpers.message(
+ 'Array must contain exactly one object of type "total"',
+ )
+ }
+ return value
+ })
+
class BasePuppetForgeUsersService extends BaseJsonService {
async fetch({ user }) {
return this._requestJson({
@@ -51,7 +63,17 @@ class BasePuppetForgeModulesService extends BaseJsonService {
}
}
-module.exports = {
+class BasePuppetForgeModulesValidationService extends BaseJsonService {
+ async fetch({ user, moduleName }) {
+ return this._requestJson({
+ schema: modulesValidationSchema,
+ url: `https://forgeapi.puppetlabs.com/private/validations/${user}-${moduleName}`,
+ })
+ }
+}
+
+export {
BasePuppetForgeModulesService,
BasePuppetForgeUsersService,
+ BasePuppetForgeModulesValidationService,
}
diff --git a/services/puppetforge/puppetforge-module-downloads.service.js b/services/puppetforge/puppetforge-module-downloads.service.js
index f5607b11d335d..c07bc1b7cfbd2 100644
--- a/services/puppetforge/puppetforge-module-downloads.service.js
+++ b/services/puppetforge/puppetforge-module-downloads.service.js
@@ -1,47 +1,37 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BasePuppetForgeModulesService } from './puppetforge-base.js'
-const { downloadCount } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { BasePuppetForgeModulesService } = require('./puppetforge-base')
+export default class PuppetforgeModuleDownloads extends BasePuppetForgeModulesService {
+ static category = 'downloads'
-module.exports = class PuppetforgeModuleDownloads extends BasePuppetForgeModulesService {
- static get category() {
- return 'downloads'
+ static route = {
+ base: 'puppetforge/dt',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'puppetforge/dt',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge downloads',
- namedParams: {
- user: 'camptocamp',
- moduleName: 'openldap',
- },
- staticPreview: this.render({ downloads: 720000 }),
+ static openApi = {
+ '/puppetforge/dt/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge downloads',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'camptocamp',
+ },
+ {
+ name: 'moduleName',
+ example: 'openldap',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
-
- static render({ downloads }) {
- return {
- message: metric(downloads),
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
async handle({ user, moduleName }) {
- const data = await this.fetch({ user, moduleName })
- return this.constructor.render({ downloads: data.downloads })
+ const { downloads } = await this.fetch({ user, moduleName })
+ return renderDownloadsBadge({ downloads })
}
}
diff --git a/services/puppetforge/puppetforge-module-downloads.tester.js b/services/puppetforge/puppetforge-module-downloads.tester.js
index ddfd0293bb372..ad5e9f10b07e5 100644
--- a/services/puppetforge/puppetforge-module-downloads.tester.js
+++ b/services/puppetforge/puppetforge-module-downloads.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('module downloads')
- .get('/camptocamp/openssl.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
+t.create('module downloads').get('/camptocamp/openssl.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
t.create('module downloads (not found)')
.get('/notarealuser/notarealpackage.json')
diff --git a/services/puppetforge/puppetforge-module-endorsement.service.js b/services/puppetforge/puppetforge-module-endorsement.service.js
index f326d4076f81b..5329e542c9735 100644
--- a/services/puppetforge/puppetforge-module-endorsement.service.js
+++ b/services/puppetforge/puppetforge-module-endorsement.service.js
@@ -1,36 +1,33 @@
-'use strict'
+import { NotFound, pathParams } from '../index.js'
+import { BasePuppetForgeModulesService } from './puppetforge-base.js'
-const { NotFound } = require('..')
-const { BasePuppetForgeModulesService } = require('./puppetforge-base')
+export default class PuppetforgeModuleEndorsement extends BasePuppetForgeModulesService {
+ static category = 'rating'
-module.exports = class PuppetforgeModuleEndorsement extends BasePuppetForgeModulesService {
- static get category() {
- return 'rating'
+ static route = {
+ base: 'puppetforge/e',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'puppetforge/e',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge endorsement',
- namedParams: {
- user: 'camptocamp',
- moduleName: 'openssl',
- },
- staticPreview: this.render({ endorsement: 'approved' }),
+ static openApi = {
+ '/puppetforge/e/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge endorsement',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'camptocamp',
+ },
+ {
+ name: 'moduleName',
+ example: 'openssl',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'endorsement' }
- }
+ static defaultBadgeData = { label: 'endorsement' }
static render({ endorsement }) {
let color
diff --git a/services/puppetforge/puppetforge-module-endorsement.tester.js b/services/puppetforge/puppetforge-module-endorsement.tester.js
index 83c5e077585a4..1b369d75d6bfc 100644
--- a/services/puppetforge/puppetforge-module-endorsement.tester.js
+++ b/services/puppetforge/puppetforge-module-endorsement.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('module endorsement')
.get('/camptocamp/openssl.json')
@@ -20,7 +19,7 @@ t.create('module endorsement (no ratings)')
feedback_score: null,
downloads: 0,
current_release: { pdk: false, version: '1.0.0' },
- })
+ }),
)
.expectBadge({
label: 'endorsement',
diff --git a/services/puppetforge/puppetforge-module-feedback.service.js b/services/puppetforge/puppetforge-module-feedback.service.js
index 02c9ce383ff67..74d0d710eee42 100644
--- a/services/puppetforge/puppetforge-module-feedback.service.js
+++ b/services/puppetforge/puppetforge-module-feedback.service.js
@@ -1,39 +1,34 @@
-'use strict'
+import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import { BasePuppetForgeModulesService } from './puppetforge-base.js'
-const {
- coveragePercentage: coveragePercentageColor,
-} = require('../color-formatters')
-const { NotFound } = require('..')
-const { BasePuppetForgeModulesService } = require('./puppetforge-base')
+export default class PuppetforgeModuleFeedback extends BasePuppetForgeModulesService {
+ static category = 'rating'
-module.exports = class PuppetforgeModuleFeedback extends BasePuppetForgeModulesService {
- static get category() {
- return 'rating'
+ static route = {
+ base: 'puppetforge/f',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'puppetforge/f',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge feedback score',
- namedParams: {
- user: 'camptocamp',
- moduleName: 'openssl',
- },
- staticPreview: this.render({ score: 61 }),
+ static openApi = {
+ '/puppetforge/f/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge feedback score',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'camptocamp',
+ },
+ {
+ name: 'moduleName',
+ example: 'openssl',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'score' }
- }
+ static defaultBadgeData = { label: 'score' }
static render({ score }) {
return {
diff --git a/services/puppetforge/puppetforge-module-feedback.tester.js b/services/puppetforge/puppetforge-module-feedback.tester.js
index c8412b8025089..ebd60fef9773b 100644
--- a/services/puppetforge/puppetforge-module-feedback.tester.js
+++ b/services/puppetforge/puppetforge-module-feedback.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('module feedback')
- .get('/camptocamp/openssl.json')
- .expectBadge({
- label: 'score',
- message: isPercentage,
- })
+t.create('module feedback').get('/camptocamp/openssl.json').expectBadge({
+ label: 'score',
+ message: isPercentage,
+})
t.create('module feedback (no ratings)')
.get('/camptocamp/openssl.json')
@@ -20,7 +17,7 @@ t.create('module feedback (no ratings)')
feedback_score: null,
downloads: 0,
current_release: { pdk: false, version: '1.0.0' },
- })
+ }),
)
.expectBadge({
label: 'score',
diff --git a/services/puppetforge/puppetforge-module-pdk-version.service.js b/services/puppetforge/puppetforge-module-pdk-version.service.js
index 087b06df0f978..7d0af582e21fb 100644
--- a/services/puppetforge/puppetforge-module-pdk-version.service.js
+++ b/services/puppetforge/puppetforge-module-pdk-version.service.js
@@ -1,37 +1,34 @@
-'use strict'
+import { renderVersionBadge } from '../version.js'
+import { NotFound, pathParams } from '../index.js'
+import { BasePuppetForgeModulesService } from './puppetforge-base.js'
-const { renderVersionBadge } = require('../version')
-const { NotFound } = require('..')
-const { BasePuppetForgeModulesService } = require('./puppetforge-base')
+export default class PuppetforgeModulePdkVersion extends BasePuppetForgeModulesService {
+ static category = 'platform-support'
-module.exports = class PuppetforgeModulePdkVersion extends BasePuppetForgeModulesService {
- static get category() {
- return 'platform-support'
+ static route = {
+ base: 'puppetforge/pdk-version',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'puppetforge/pdk-version',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge – PDK version',
- namedParams: {
- user: 'tragiccode',
- moduleName: 'azure_key_vault',
- },
- staticPreview: renderVersionBadge({ version: '1.7.1' }),
+ static openApi = {
+ '/puppetforge/pdk-version/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge - PDK version',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'tragiccode',
+ },
+ {
+ name: 'moduleName',
+ example: 'azure_key_vault',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'pdk version' }
- }
+ static defaultBadgeData = { label: 'pdk version' }
async handle({ user, moduleName }) {
const data = await this.fetch({ user, moduleName })
diff --git a/services/puppetforge/puppetforge-module-pdk-version.tester.js b/services/puppetforge/puppetforge-module-pdk-version.tester.js
index f4a94e7116489..cbcd15d9b5c5e 100644
--- a/services/puppetforge/puppetforge-module-pdk-version.tester.js
+++ b/services/puppetforge/puppetforge-module-pdk-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('PDK version')
- .get('/tragiccode/azure_key_vault.json')
- .expectBadge({
- label: 'pdk version',
- message: isSemver,
- })
+t.create('PDK version').get('/tragiccode/azure_key_vault.json').expectBadge({
+ label: 'pdk version',
+ message: isSemver,
+})
t.create("PDK version (library doesn't use the PDK)")
.get('/puppet/yum.json')
diff --git a/services/puppetforge/puppetforge-module-quality-score.service.js b/services/puppetforge/puppetforge-module-quality-score.service.js
new file mode 100644
index 0000000000000..8b1e4b9b19c59
--- /dev/null
+++ b/services/puppetforge/puppetforge-module-quality-score.service.js
@@ -0,0 +1,45 @@
+import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js'
+import { pathParams } from '../index.js'
+import { BasePuppetForgeModulesValidationService } from './puppetforge-base.js'
+
+export default class PuppetforgeModuleQualityScoreService extends BasePuppetForgeModulesValidationService {
+ static category = 'rating'
+
+ static route = {
+ base: 'puppetforge/qualityscore',
+ pattern: ':user/:moduleName',
+ }
+
+ static openApi = {
+ '/puppetforge/qualityscore/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge quality score',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'camptocamp',
+ },
+ {
+ name: 'moduleName',
+ example: 'openssl',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'quality score' }
+
+ static render({ score }) {
+ return {
+ message: `${score}%`,
+ color: coveragePercentageColor(score),
+ }
+ }
+
+ async handle({ user, moduleName }) {
+ const data = await this.fetch({ user, moduleName })
+ const qualityScore = data.find(el => el.name === 'total').score
+ return this.constructor.render({ score: qualityScore })
+ }
+}
diff --git a/services/puppetforge/puppetforge-module-quality-score.tester.js b/services/puppetforge/puppetforge-module-quality-score.tester.js
new file mode 100644
index 0000000000000..9693b4fcc2e5f
--- /dev/null
+++ b/services/puppetforge/puppetforge-module-quality-score.tester.js
@@ -0,0 +1,27 @@
+import { isPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('module quality-score').get('/camptocamp/openssl.json').expectBadge({
+ label: 'quality score',
+ message: isPercentage,
+})
+
+t.create('module quality score (no ratings)')
+ .get('/camptocamp/openssl.json')
+ .intercept(nock =>
+ nock('https://forgeapi.puppetlabs.com/private/validations')
+ .get('/camptocamp-openssl')
+ .reply(200, []),
+ )
+ .expectBadge({
+ label: 'quality score',
+ message: 'invalid response data',
+ })
+
+t.create('module quality score (not found)')
+ .get('/notarealuser/notarealpackage.json')
+ .expectBadge({
+ label: 'quality score',
+ message: 'not found',
+ })
diff --git a/services/puppetforge/puppetforge-module-version.service.js b/services/puppetforge/puppetforge-module-version.service.js
index 14e5b90ae5afc..4bedf34d17c8f 100644
--- a/services/puppetforge/puppetforge-module-version.service.js
+++ b/services/puppetforge/puppetforge-module-version.service.js
@@ -1,36 +1,34 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BasePuppetForgeModulesService } from './puppetforge-base.js'
-const { renderVersionBadge } = require('../version')
-const { BasePuppetForgeModulesService } = require('./puppetforge-base')
+export default class PuppetforgeModuleVersion extends BasePuppetForgeModulesService {
+ static category = 'version'
-module.exports = class PuppetforgeModuleVersion extends BasePuppetForgeModulesService {
- static get category() {
- return 'version'
+ static route = {
+ base: 'puppetforge/v',
+ pattern: ':user/:moduleName',
}
- static get route() {
- return {
- base: 'puppetforge/v',
- pattern: ':user/:moduleName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge version',
- namedParams: {
- user: 'vStone',
- moduleName: 'percona',
- },
- staticPreview: renderVersionBadge({ version: '1.3.3' }),
+ static openApi = {
+ '/puppetforge/v/{user}/{moduleName}': {
+ get: {
+ summary: 'Puppet Forge version',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'vStone',
+ },
+ {
+ name: 'moduleName',
+ example: 'percona',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'puppetforge' }
- }
+ static defaultBadgeData = { label: 'puppetforge' }
async handle({ user, moduleName }) {
const data = await this.fetch({ user, moduleName })
diff --git a/services/puppetforge/puppetforge-module-version.tester.js b/services/puppetforge/puppetforge-module-version.tester.js
index 5a9720912d22b..79117a314e308 100644
--- a/services/puppetforge/puppetforge-module-version.tester.js
+++ b/services/puppetforge/puppetforge-module-version.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('module version')
- .get('/camptocamp/openssl.json')
- .expectBadge({
- label: 'puppetforge',
- message: isSemver,
- })
+t.create('module version').get('/camptocamp/openssl.json').expectBadge({
+ label: 'puppetforge',
+ message: isSemver,
+})
t.create('module version (not found)')
.get('/notarealuser/notarealpackage.json')
diff --git a/services/puppetforge/puppetforge-user-module-count.service.js b/services/puppetforge/puppetforge-user-module-count.service.js
index 3fd3f3cec39ba..3f43d219a9b11 100644
--- a/services/puppetforge/puppetforge-user-module-count.service.js
+++ b/services/puppetforge/puppetforge-user-module-count.service.js
@@ -1,36 +1,29 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { BasePuppetForgeUsersService } from './puppetforge-base.js'
-const { metric } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BasePuppetForgeUsersService } = require('./puppetforge-base')
+export default class PuppetForgeModuleCountService extends BasePuppetForgeUsersService {
+ static category = 'other'
-module.exports = class PuppetForgeModuleCountService extends BasePuppetForgeUsersService {
- static get category() {
- return 'other'
+ static route = {
+ base: 'puppetforge/mc',
+ pattern: ':user',
}
- static get route() {
- return {
- base: 'puppetforge/mc',
- pattern: ':user',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge modules by user',
- namedParams: {
- user: 'camptocamp',
- },
- staticPreview: this.render({ modules: 60 }),
+ static openApi = {
+ '/puppetforge/mc/{user}': {
+ get: {
+ summary: 'Puppet Forge modules by user',
+ parameters: pathParams({
+ name: 'user',
+ example: 'camptocamp',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'modules' }
- }
+ static defaultBadgeData = { label: 'modules' }
static render({ modules }) {
return {
diff --git a/services/puppetforge/puppetforge-user-module-count.tester.js b/services/puppetforge/puppetforge-user-module-count.tester.js
index a5f871194b6dd..2d999d1078416 100644
--- a/services/puppetforge/puppetforge-user-module-count.tester.js
+++ b/services/puppetforge/puppetforge-user-module-count.tester.js
@@ -1,18 +1,13 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('modules by user').get('/camptocamp.json').expectBadge({
+ label: 'modules',
+ message: isMetric,
+})
-t.create('modules by user')
- .get('/camptocamp.json')
- .expectBadge({
- label: 'modules',
- message: isMetric,
- })
-
-t.create('modules by user')
- .get('/not-a-real-user.json')
- .expectBadge({
- label: 'modules',
- message: 'not found',
- })
+t.create('modules by user').get('/not-a-real-user.json').expectBadge({
+ label: 'modules',
+ message: 'not found',
+})
diff --git a/services/puppetforge/puppetforge-user-release-count.service.js b/services/puppetforge/puppetforge-user-release-count.service.js
index babd938d16118..2e4c4d42331be 100644
--- a/services/puppetforge/puppetforge-user-release-count.service.js
+++ b/services/puppetforge/puppetforge-user-release-count.service.js
@@ -1,36 +1,29 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { BasePuppetForgeUsersService } from './puppetforge-base.js'
-const { metric } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BasePuppetForgeUsersService } = require('./puppetforge-base')
+export default class PuppetForgeReleaseCountService extends BasePuppetForgeUsersService {
+ static category = 'other'
-module.exports = class PuppetForgeReleaseCountService extends BasePuppetForgeUsersService {
- static get category() {
- return 'other'
+ static route = {
+ base: 'puppetforge/rc',
+ pattern: ':user',
}
- static get route() {
- return {
- base: 'puppetforge/rc',
- pattern: ':user',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Puppet Forge releases by user',
- namedParams: {
- user: 'camptocamp',
- },
- staticPreview: this.render({ releases: 1000 }),
+ static openApi = {
+ '/puppetforge/rc/{user}': {
+ get: {
+ summary: 'Puppet Forge releases by user',
+ parameters: pathParams({
+ name: 'user',
+ example: 'camptocamp',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'releases' }
- }
+ static defaultBadgeData = { label: 'releases' }
static render({ releases }) {
return {
diff --git a/services/puppetforge/puppetforge-user-release-count.tester.js b/services/puppetforge/puppetforge-user-release-count.tester.js
index 13ba3ef1ed079..f8a8d60028c05 100644
--- a/services/puppetforge/puppetforge-user-release-count.tester.js
+++ b/services/puppetforge/puppetforge-user-release-count.tester.js
@@ -1,18 +1,13 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('releases by user').get('/camptocamp.json').expectBadge({
+ label: 'releases',
+ message: isMetric,
+})
-t.create('releases by user')
- .get('/camptocamp.json')
- .expectBadge({
- label: 'releases',
- message: isMetric,
- })
-
-t.create('releases by user')
- .get('/not-a-real-user.json')
- .expectBadge({
- label: 'releases',
- message: 'not found',
- })
+t.create('releases by user').get('/not-a-real-user.json').expectBadge({
+ label: 'releases',
+ message: 'not found',
+})
diff --git a/services/pypi/pypi-base.js b/services/pypi/pypi-base.js
index f77b7cf5fcbcb..e8c4c597e9c43 100644
--- a/services/pypi/pypi-base.js
+++ b/services/pypi/pypi-base.js
@@ -1,44 +1,62 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import config from 'config'
+import { optionalUrl } from '../validators.js'
+import { BaseJsonService, queryParam, pathParam } from '../index.js'
const schema = Joi.object({
info: Joi.object({
version: Joi.string().required(),
// https://github.com/badges/shields/issues/2022
- license: Joi.string().allow(''),
- classifiers: Joi.array()
- .items(Joi.string())
- .required(),
+ // https://github.com/badges/shields/issues/7728
+ license: Joi.string().allow('').allow(null),
+ license_expression: Joi.string().allow('').allow(null),
+ classifiers: Joi.array().items(Joi.string()).required(),
}).required(),
- releases: Joi.object()
- .pattern(
- Joi.string(),
- Joi.array()
- .items(
- Joi.object({
- packagetype: Joi.string().required(),
- })
- )
- .required()
+ urls: Joi.array()
+ .items(
+ Joi.object({
+ packagetype: Joi.string().required(),
+ }),
)
.required(),
}).required()
-module.exports = class PypiBase extends BaseJsonService {
+export const queryParamSchema = Joi.object({
+ pypiBaseUrl: optionalUrl,
+}).required()
+
+export const pypiPackageParam = pathParam({
+ name: 'packageName',
+ example: 'Django',
+})
+
+export const pypiBaseUrlParam = queryParam({
+ name: 'pypiBaseUrl',
+ example: 'https://pypi.org',
+})
+
+export const pypiGeneralParams = [pypiPackageParam, pypiBaseUrlParam]
+
+export default class PypiBase extends BaseJsonService {
+ constructor(...args) {
+ super(...args)
+ this._defaultPypiBaseUrl =
+ config.util.toObject().public.services.pypi.baseUri
+ }
+
static buildRoute(base) {
return {
base,
- pattern: ':egg*',
+ pattern: ':egg+',
+ queryParamSchema,
}
}
- async fetch({ egg }) {
+ async fetch({ egg, pypiBaseUrl = this._defaultPypiBaseUrl }) {
return this._requestJson({
schema,
- url: `https://pypi.org/pypi/${egg}/json`,
- errorMessages: { 404: 'package or version not found' },
+ url: `${pypiBaseUrl}/pypi/${egg}/json`,
+ httpErrors: { 404: 'package or version not found' },
})
}
}
diff --git a/services/pypi/pypi-django-versions.service.js b/services/pypi/pypi-django-versions.service.js
index d2ab1e54d038f..da07cf8f0cf9c 100644
--- a/services/pypi/pypi-django-versions.service.js
+++ b/services/pypi/pypi-django-versions.service.js
@@ -1,55 +1,12 @@
-'use strict'
-
-const PypiBase = require('./pypi-base')
-const { sortDjangoVersions, parseClassifiers } = require('./pypi-helpers')
-
-module.exports = class PypiDjangoVersions extends PypiBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return this.buildRoute('pypi/djversions')
- }
-
- static get examples() {
- return [
- {
- title: 'PyPI - Django Version',
- pattern: ':packageName',
- namedParams: { packageName: 'djangorestframework' },
- staticPreview: this.render({ versions: ['1.11', '2.0', '2.1'] }),
- keywords: ['python'],
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'django versions' }
- }
-
- static render({ versions }) {
- if (versions.length > 0) {
- return {
- message: sortDjangoVersions(versions).join(' | '),
- color: 'blue',
- }
- } else {
- return {
- message: 'missing',
- color: 'red',
- }
- }
- }
-
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
-
- const versions = parseClassifiers(
- packageData,
- /^Framework :: Django :: ([\d.]+)$/
- )
-
- return this.constructor.render({ versions })
- }
-}
+import { redirector } from '../index.js'
+
+export default redirector({
+ category: 'platform-support',
+ route: {
+ base: 'pypi/djversions',
+ pattern: ':packageName*',
+ },
+ transformPath: ({ packageName }) =>
+ `/pypi/frameworkversions/django/${packageName}`,
+ dateAdded: new Date('2022-07-28'),
+})
diff --git a/services/pypi/pypi-django-versions.tester.js b/services/pypi/pypi-django-versions.tester.js
index 21d60d38b1a78..c3901a3a0854c 100644
--- a/services/pypi/pypi-django-versions.tester.js
+++ b/services/pypi/pypi-django-versions.tester.js
@@ -1,33 +1,24 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isPipeSeparatedDjangoVersions = Joi.string().regex(
- /^([0-9]+\.[0-9]+(?: \| )?)+$/
+t.create(
+ 'redirect supported django versions (valid, package version in request)',
)
-
-t.create('supported django versions (valid, package version in request)')
.get('/djangorestframework/3.7.3.json')
- .expectBadge({
- label: 'django versions',
- message: isPipeSeparatedDjangoVersions,
- })
+ .expectRedirect(
+ '/pypi/frameworkversions/django/djangorestframework/3.7.3.json',
+ )
-t.create('supported django versions (valid, no package version specified)')
+t.create(
+ 'redirect supported django versions (valid, no package version specified)',
+)
.get('/djangorestframework.json')
- .expectBadge({
- label: 'django versions',
- message: isPipeSeparatedDjangoVersions,
- })
+ .expectRedirect('/pypi/frameworkversions/django/djangorestframework.json')
-t.create('supported django versions (no versions specified)')
+t.create('redirect supported django versions (no versions specified)')
.get('/django/1.11.json')
- .expectBadge({ label: 'django versions', message: 'missing' })
+ .expectRedirect('/pypi/frameworkversions/django/django/1.11.json')
-t.create('supported django versions (invalid)')
+t.create('redirect supported django versions (invalid)')
.get('/not-a-package.json')
- .expectBadge({
- label: 'django versions',
- message: 'package or version not found',
- })
+ .expectRedirect('/pypi/frameworkversions/django/not-a-package.json')
diff --git a/services/pypi/pypi-downloads.service.js b/services/pypi/pypi-downloads.service.js
index c592bca47c6f7..bb99d3dff9eab 100644
--- a/services/pypi/pypi-downloads.service.js
+++ b/services/pypi/pypi-downloads.service.js
@@ -1,12 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { downloadCount } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const keywords = ['python']
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParam } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { pypiPackageParam } from './pypi-base.js'
const schema = Joi.object({
data: Joi.object({
@@ -18,71 +14,66 @@ const schema = Joi.object({
const periodMap = {
dd: {
- api_field: 'last_day',
- suffix: '/day',
+ apiField: 'last_day',
+ interval: 'day',
},
dw: {
- api_field: 'last_week',
- suffix: '/week',
+ apiField: 'last_week',
+ interval: 'week',
},
dm: {
- api_field: 'last_month',
- suffix: '/month',
+ apiField: 'last_month',
+ interval: 'month',
},
}
// this badge uses PyPI Stats instead of the PyPI API
// so it doesn't extend PypiBase
-module.exports = class PypiDownloads extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
+export default class PypiDownloads extends BaseJsonService {
+ static category = 'downloads'
- static get route() {
- return {
- base: 'pypi',
- pattern: ':period(dd|dw|dm)/:packageName',
- }
+ static route = {
+ base: 'pypi',
+ pattern: ':period(dd|dw|dm)/:packageName',
}
- static get examples() {
- return [
- {
- title: 'PyPI - Downloads',
- namedParams: {
- period: 'dd',
- packageName: 'Django',
- },
- staticPreview: this.render({ period: 'dd', downloads: 14000 }),
- keywords,
+ static openApi = {
+ '/pypi/{period}/{packageName}': {
+ get: {
+ summary: 'PyPI Downloads',
+ description:
+ 'Python package downloads from [pypistats](https://pypistats.org/)',
+ parameters: [
+ pathParam({
+ name: 'period',
+ example: 'dd',
+ schema: { type: 'string', enum: this.getEnum('period') },
+ description: 'Daily, Weekly, or Monthly downloads',
+ }),
+ pypiPackageParam,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static _cacheLength = 43200
- static render({ period, downloads }) {
- return {
- message: `${metric(downloads)}${periodMap[period].suffix}`,
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
async fetch({ packageName }) {
return this._requestJson({
url: `https://pypistats.org/api/packages/${packageName.toLowerCase()}/recent`,
schema,
- errorMessages: { 404: 'package not found' },
+ httpErrors: { 404: 'package not found' },
})
}
async handle({ period, packageName }) {
- const json = await this.fetch({ packageName })
- return this.constructor.render({
- period,
- downloads: json.data[periodMap[period].api_field],
+ const { apiField, interval } = periodMap[period]
+ const { data } = await this.fetch({ packageName })
+ return renderDownloadsBadge({
+ downloads: data[apiField],
+ interval,
})
}
}
diff --git a/services/pypi/pypi-downloads.tester.js b/services/pypi/pypi-downloads.tester.js
index c8e17b5ea8dca..5e4b57fe987c2 100644
--- a/services/pypi/pypi-downloads.tester.js
+++ b/services/pypi/pypi-downloads.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetricOverTimePeriod } = require('../test-validators')
+import { createServiceTester } from '../tester.js'
+import { isMetricOverTimePeriod } from '../test-validators.js'
+export const t = await createServiceTester()
t.create('daily downloads (valid)')
.get('/dd/djangorestframework.json')
diff --git a/services/pypi/pypi-format.service.js b/services/pypi/pypi-format.service.js
index ea64f04271ddb..ee1aab077b6b5 100644
--- a/services/pypi/pypi-format.service.js
+++ b/services/pypi/pypi-format.service.js
@@ -1,32 +1,23 @@
-'use strict'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { getPackageFormats } from './pypi-helpers.js'
-const PypiBase = require('./pypi-base')
-const { getPackageFormats } = require('./pypi-helpers')
+export default class PypiFormat extends PypiBase {
+ static category = 'other'
-module.exports = class PypiFormat extends PypiBase {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return this.buildRoute('pypi/format')
- }
+ static route = this.buildRoute('pypi/format')
- static get examples() {
- return [
- {
- title: 'PyPI - Format',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ hasWheel: true }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/format/{packageName}': {
+ get: {
+ summary: 'PyPI Format',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'format' }
- }
+ static _cacheLength = 43200
+
+ static defaultBadgeData = { label: 'format' }
static render({ hasWheel, hasEgg }) {
if (hasWheel) {
@@ -47,8 +38,8 @@ module.exports = class PypiFormat extends PypiBase {
}
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
const { hasWheel, hasEgg } = getPackageFormats(packageData)
return this.constructor.render({ hasWheel, hasEgg })
}
diff --git a/services/pypi/pypi-format.tester.js b/services/pypi/pypi-format.tester.js
index ffc93f9ea58ea..ba0230b8361b4 100644
--- a/services/pypi/pypi-format.tester.js
+++ b/services/pypi/pypi-format.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('format (wheel, package version in request)')
.get('/requests/2.18.4.json')
@@ -21,3 +20,22 @@ t.create('format (egg)')
t.create('format (invalid)')
.get('/not-a-package.json')
.expectBadge({ label: 'format', message: 'package or version not found' })
+
+t.create('format (explicit pypi base url)')
+ .get('/requests/2.18.4.json?pypiBaseUrl=https://some-other-pypi.org')
+ .intercept(nock =>
+ nock('https://some-other-pypi.org')
+ .get('/pypi/requests/2.18.4/json')
+ .reply(200, {
+ info: {
+ version: '2.18.4',
+ classifiers: [],
+ },
+ urls: [
+ {
+ packagetype: 'bdist_wheel',
+ },
+ ],
+ }),
+ )
+ .expectBadge({ label: 'format', message: 'wheel' })
diff --git a/services/pypi/pypi-framework-versions.service.js b/services/pypi/pypi-framework-versions.service.js
new file mode 100644
index 0000000000000..c560f4e535b76
--- /dev/null
+++ b/services/pypi/pypi-framework-versions.service.js
@@ -0,0 +1,104 @@
+import { InvalidResponse, pathParams } from '../index.js'
+import PypiBase, { pypiBaseUrlParam } from './pypi-base.js'
+import { sortPypiVersions, parseClassifiers } from './pypi-helpers.js'
+
+const frameworkNameMap = {
+ 'aws-cdk': {
+ name: 'AWS CDK',
+ classifier: 'AWS CDK',
+ },
+ django: {
+ name: 'Django',
+ classifier: 'Django',
+ },
+ 'django-cms': {
+ name: 'Django CMS',
+ classifier: 'Django CMS',
+ },
+ jupyterlab: {
+ name: 'JupyterLab',
+ classifier: 'Jupyter :: JupyterLab',
+ },
+ odoo: {
+ name: 'Odoo',
+ classifier: 'Odoo',
+ },
+ plone: {
+ name: 'Plone',
+ classifier: 'Plone',
+ },
+ wagtail: {
+ name: 'Wagtail',
+ classifier: 'Wagtail',
+ },
+ zope: {
+ name: 'Zope',
+ classifier: 'Zope',
+ },
+}
+
+const description = `
+This service currently support the following Frameworks:
+${Object.values(frameworkNameMap).map(obj => ` ${obj.name} `)}
+`
+export default class PypiFrameworkVersion extends PypiBase {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'pypi/frameworkversions',
+ pattern: `:frameworkName(${Object.keys(frameworkNameMap).join(
+ '|',
+ )})/:packageName+`,
+ }
+
+ static openApi = {
+ '/pypi/frameworkversions/{frameworkName}/{packageName}': {
+ get: {
+ summary: 'PyPI Versions from Framework Classifiers',
+ description,
+ parameters: pathParams(
+ {
+ name: 'frameworkName',
+ example: 'plone',
+ schema: { type: 'string', enum: Object.keys(frameworkNameMap) },
+ },
+ { name: 'packageName', example: 'plone.volto' },
+ ).concat(pypiBaseUrlParam),
+ },
+ },
+ }
+
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'versions' }
+
+ static render({ name, versions }) {
+ name = name ? name.toLowerCase() : ''
+ const label = `${name} versions`
+ return {
+ label,
+ message: sortPypiVersions(versions).join(' | '),
+ color: 'blue',
+ }
+ }
+
+ async handle({ frameworkName, packageName }, { pypiBaseUrl }) {
+ const classifier = frameworkNameMap[frameworkName]
+ ? frameworkNameMap[frameworkName].classifier
+ : frameworkName
+ const name = frameworkNameMap[frameworkName]
+ ? frameworkNameMap[frameworkName].name
+ : frameworkName
+ const regex = new RegExp(`^Framework :: ${classifier} :: ([\\d.]+)$`)
+ const packageData = await this.fetch({ egg: packageName, pypiBaseUrl })
+ const versions = parseClassifiers(packageData, regex)
+
+ if (versions.length === 0) {
+ throw new InvalidResponse({
+ prettyMessage: `${name} versions are missing for ${packageName}`,
+ })
+ }
+
+ return this.constructor.render({ name, versions })
+ }
+}
diff --git a/services/pypi/pypi-framework-versions.tester.js b/services/pypi/pypi-framework-versions.tester.js
new file mode 100644
index 0000000000000..e65cb73e36d27
--- /dev/null
+++ b/services/pypi/pypi-framework-versions.tester.js
@@ -0,0 +1,164 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+const isPipeSeparatedFrameworkVersions = Joi.string().regex(
+ /^([1-9]+(\.[0-9]+)?(?: \| )?)+$/,
+)
+
+t.create('supported django versions (valid, package version in request)')
+ .get('/django/djangorestframework/3.7.3.json')
+ .expectBadge({
+ label: 'django versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported django versions (valid, no package version specified)')
+ .get('/django/djangorestframework.json')
+ .expectBadge({
+ label: 'django versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported django versions (no versions specified)')
+ .get('/django/django/1.11.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'Django versions are missing for django/1.11',
+ })
+
+t.create('supported django versions (invalid)')
+ .get('/django/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported plone versions (valid, package version in request)')
+ .get('/plone/plone.rest/1.6.2.json')
+ .expectBadge({ label: 'plone versions', message: '4.3 | 5.0 | 5.1 | 5.2' })
+
+t.create('supported plone versions (valid, no package version specified)')
+ .get('/plone/plone.rest.json')
+ .expectBadge({
+ label: 'plone versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported plone versions (invalid)')
+ .get('/plone/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported zope versions (valid, package version in request)')
+ .get('/zope/plone/5.2.9.json')
+ .expectBadge({ label: 'zope versions', message: '4' })
+
+t.create('supported zope versions (valid, no package version specified)')
+ .get('/zope/Plone.json')
+ .expectBadge({
+ label: 'zope versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported zope versions (invalid)')
+ .get('/zope/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported wagtail versions (valid, package version in request)')
+ .get('/wagtail/wagtail-headless-preview/0.3.0.json')
+ .expectBadge({ label: 'wagtail versions', message: '2 | 3' })
+
+t.create('supported wagtail versions (valid, no package version specified)')
+ .get('/wagtail/wagtail-headless-preview.json')
+ .expectBadge({
+ label: 'wagtail versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported wagtail versions (invalid)')
+ .get('/wagtail/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported django cms versions (valid, package version in request)')
+ .get('/django-cms/djangocms-ads/1.1.0.json')
+ .expectBadge({
+ label: 'django cms versions',
+ message: '3.7 | 3.8 | 3.9 | 3.10',
+ })
+
+t.create('supported django cms versions (valid, no package version specified)')
+ .get('/django-cms/djangocms-ads.json')
+ .expectBadge({
+ label: 'django cms versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported django cms versions (invalid)')
+ .get('/django-cms/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported odoo versions (valid, package version in request)')
+ .get('/odoo/odoo-addon-sale-tier-validation/15.0.1.0.0.6.json')
+ .expectBadge({ label: 'odoo versions', message: '15.0' })
+
+t.create('supported odoo versions (valid, no package version specified)')
+ .get('/odoo/odoo-addon-sale-tier-validation.json')
+ .expectBadge({
+ label: 'odoo versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported odoo versions (invalid)')
+ .get('/odoo/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported aws cdk versions (valid, package version in request)')
+ .get('/aws-cdk/aws-cdk.aws-glue-alpha/2.34.0a0.json')
+ .expectBadge({ label: 'aws cdk versions', message: '2' })
+
+t.create('supported aws cdk versions (valid, no package version specified)')
+ .get('/aws-cdk/aws-cdk.aws-glue-alpha.json')
+ .expectBadge({
+ label: 'aws cdk versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported aws cdk versions (invalid)')
+ .get('/aws-cdk/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
+
+t.create('supported jupyterlab versions (valid, package version in request)')
+ .get('/jupyterlab/structured-text/0.0.2.json')
+ .expectBadge({ label: 'jupyterlab versions', message: '3' })
+
+t.create('supported jupyterlab versions (valid, no package version specified)')
+ .get('/jupyterlab/structured-text.json')
+ .expectBadge({
+ label: 'jupyterlab versions',
+ message: isPipeSeparatedFrameworkVersions,
+ })
+
+t.create('supported jupyterlab versions (invalid)')
+ .get('/jupyterlab/not-a-package.json')
+ .expectBadge({
+ label: 'versions',
+ message: 'package or version not found',
+ })
diff --git a/services/pypi/pypi-helpers.js b/services/pypi/pypi-helpers.js
index 0a108c9b07c28..2b18c5fc949f2 100644
--- a/services/pypi/pypi-helpers.js
+++ b/services/pypi/pypi-helpers.js
@@ -1,5 +1,3 @@
-'use strict'
-
/*
Django versions will be specified in the form major.minor
trying to sort with `semver.compare` will throw e.g:
@@ -8,7 +6,7 @@
our own functions to parse and sort django versions
*/
-function parseDjangoVersionString(str) {
+function parsePypiVersionString(str) {
if (typeof str !== 'string') {
return false
}
@@ -22,46 +20,70 @@ function parseDjangoVersionString(str) {
}
// Sort an array of django versions low to high.
-function sortDjangoVersions(versions) {
+function sortPypiVersions(versions) {
return versions.sort((a, b) => {
- if (
- parseDjangoVersionString(a).major === parseDjangoVersionString(b).major
- ) {
- return (
- parseDjangoVersionString(a).minor - parseDjangoVersionString(b).minor
- )
+ if (parsePypiVersionString(a).major === parsePypiVersionString(b).major) {
+ return parsePypiVersionString(a).minor - parsePypiVersionString(b).minor
} else {
- return (
- parseDjangoVersionString(a).major - parseDjangoVersionString(b).major
- )
+ return parsePypiVersionString(a).major - parsePypiVersionString(b).major
}
})
}
// Extract classifiers from a pypi json response based on a regex.
-function parseClassifiers(parsedData, pattern) {
+function parseClassifiers(parsedData, pattern, preserveCase = false) {
const results = []
for (let i = 0; i < parsedData.info.classifiers.length; i++) {
const matched = pattern.exec(parsedData.info.classifiers[i])
if (matched && matched[1]) {
- results.push(matched[1].toLowerCase())
+ if (preserveCase) {
+ results.push(matched[1])
+ } else {
+ results.push(matched[1].toLowerCase())
+ }
}
}
return results
}
function getLicenses(packageData) {
- const {
- info: { license },
- } = packageData
- if (license) {
+ const license = packageData.info.license
+ const licenseExpression = packageData.info.license_expression
+
+ if (licenseExpression) {
+ /*
+ The .license_expression field contains an SPDX expression, and it
+ is the preferred way of documenting a Python project's license.
+ See https://peps.python.org/pep-0639/
+ */
+ return [licenseExpression]
+ } else if (license && license.length < 40) {
+ /*
+ The .license field may either contain
+ - a short license description (e.g: 'MIT' or 'GPL-3.0') or
+ - the full text of a license
+ but there is nothing in the response that tells us explicitly.
+ We have to make an assumption based on the length.
+ See https://github.com/badges/shields/issues/8689 and
+ https://github.com/badges/shields/pull/8690 for more info.
+ */
return [license]
} else {
+ // else fall back to trove classifiers
const parenthesizedAcronymRegex = /\(([^)]+)\)/
const bareAcronymRegex = /^[a-z0-9]+$/
- let licenses = parseClassifiers(packageData, /^License :: (.+)$/)
+ const spdxAliases = {
+ 'OSI Approved :: Apache Software License': 'Apache-2.0',
+ 'CC0 1.0 Universal (CC0 1.0) Public Domain Dedication': 'CC0-1.0',
+ 'OSI Approved :: GNU Affero General Public License v3': 'AGPL-3.0',
+ 'OSI Approved :: Zero-Clause BSD (0BSD)': '0BSD',
+ }
+ let licenses = parseClassifiers(packageData, /^License :: (.+)$/, true)
+ .map(classifier =>
+ classifier in spdxAliases ? spdxAliases[classifier] : classifier,
+ )
.map(classifier => classifier.split(' :: ').pop())
- .map(license => license.replace(' license', ''))
+ .map(license => license.replace(' License', ''))
.map(license => {
const match = license.match(parenthesizedAcronymRegex)
return match ? match[1].toUpperCase() : license
@@ -71,32 +93,28 @@ function getLicenses(packageData) {
return match ? license.toUpperCase() : license
})
if (licenses.length > 1) {
- licenses = licenses.filter(l => l !== 'dfsg approved')
+ licenses = licenses.filter(l => l !== 'DFSG approved')
}
return licenses
}
}
function getPackageFormats(packageData) {
- const {
- info: { version },
- releases,
- } = packageData
- const releasesForVersion = releases[version]
+ const { urls } = packageData
return {
- hasWheel: releasesForVersion.some(({ packagetype }) =>
- ['wheel', 'bdist_wheel'].includes(packagetype)
+ hasWheel: urls.some(({ packagetype }) =>
+ ['wheel', 'bdist_wheel'].includes(packagetype),
),
- hasEgg: releasesForVersion.some(({ packagetype }) =>
- ['egg', 'bdist_egg'].includes(packagetype)
+ hasEgg: urls.some(({ packagetype }) =>
+ ['egg', 'bdist_egg'].includes(packagetype),
),
}
}
-module.exports = {
+export {
parseClassifiers,
- parseDjangoVersionString,
- sortDjangoVersions,
+ parsePypiVersionString,
+ sortPypiVersions,
getLicenses,
getPackageFormats,
}
diff --git a/services/pypi/pypi-helpers.spec.js b/services/pypi/pypi-helpers.spec.js
index e121639229ba4..5b57a57ba4369 100644
--- a/services/pypi/pypi-helpers.spec.js
+++ b/services/pypi/pypi-helpers.spec.js
@@ -1,13 +1,11 @@
-'use strict'
-
-const { test, given, forCases } = require('sazerac')
-const {
+import { test, given, forCases } from 'sazerac'
+import {
parseClassifiers,
- parseDjangoVersionString,
- sortDjangoVersions,
+ parsePypiVersionString,
+ sortPypiVersions,
getLicenses,
getPackageFormats,
-} = require('./pypi-helpers')
+} from './pypi-helpers.js'
const classifiersFixture = {
info: {
@@ -36,11 +34,11 @@ const classifiersFixture = {
},
}
-describe('PyPI helpers', function() {
- test(parseClassifiers, function() {
+describe('PyPI helpers', function () {
+ test(parseClassifiers, () => {
given(
classifiersFixture,
- /^Programming Language :: Python :: ([\d.]+)$/
+ /^Programming Language :: Python :: ([\d.]+)$/,
).expect(['2', '2.7', '3', '3.4', '3.5', '3.6'])
given(classifiersFixture, /^Framework :: Django :: ([\d.]+)$/).expect([
@@ -50,19 +48,19 @@ describe('PyPI helpers', function() {
given(
classifiersFixture,
- /^Programming Language :: Python :: Implementation :: (\S+)$/
+ /^Programming Language :: Python :: Implementation :: (\S+)$/,
).expect(['cpython', 'pypy'])
// regex that matches everything
given(classifiersFixture, /^([\S\s+]+)$/).expect(
- classifiersFixture.info.classifiers.map(e => e.toLowerCase())
+ classifiersFixture.info.classifiers.map(e => e.toLowerCase()),
)
// regex that matches nothing
given(classifiersFixture, /^(?!.*)*$/).expect([])
})
- test(parseDjangoVersionString, function() {
+ test(parsePypiVersionString, () => {
given('1').expect({ major: 1, minor: 0 })
given('1.0').expect({ major: 1, minor: 0 })
given('7.2').expect({ major: 7, minor: 2 })
@@ -71,7 +69,7 @@ describe('PyPI helpers', function() {
given('foo').expect({ major: 0, minor: 0 })
})
- test(sortDjangoVersions, function() {
+ test(sortPypiVersions, () => {
// Each of these includes a different variant: 2.0, 2, and 2.0rc1.
given(['2.0', '1.9', '10', '1.11', '2.1', '2.11']).expect([
'1.9',
@@ -102,19 +100,47 @@ describe('PyPI helpers', function() {
})
test(getLicenses, () => {
- forCases([given({ info: { license: 'MIT', classifiers: [] } })]).expect([
- 'MIT',
- ])
forCases([
+ given({
+ info: {
+ license: null,
+ license_expression: 'MIT',
+ classifiers: [],
+ },
+ }),
+ given({
+ info: {
+ license: 'MIT',
+ license_expression: null,
+ classifiers: [],
+ },
+ }),
+ given({
+ info: {
+ license: null,
+ license_expression: null,
+ classifiers: ['License :: OSI Approved :: MIT License'],
+ },
+ }),
given({
info: {
license: '',
+ license_expression: null,
+ classifiers: ['License :: OSI Approved :: MIT License'],
+ },
+ }),
+ given({
+ info: {
+ license:
+ 'this text is really really really really really really long',
+ license_expression: null,
classifiers: ['License :: OSI Approved :: MIT License'],
},
}),
given({
info: {
license: '',
+ license_expression: null,
classifiers: [
'License :: OSI Approved :: MIT License',
'License :: DFSG approved',
@@ -125,47 +151,64 @@ describe('PyPI helpers', function() {
given({
info: {
license: '',
+ license_expression: null,
classifiers: ['License :: Public Domain'],
},
- }).expect(['public domain'])
+ }).expect(['Public Domain'])
given({
info: {
license: '',
+ license_expression: null,
classifiers: ['License :: Netscape Public License (NPL)'],
},
}).expect(['NPL'])
+ given({
+ info: {
+ license: '',
+ license_expression: null,
+ classifiers: ['License :: OSI Approved :: Apache Software License'],
+ },
+ }).expect(['Apache-2.0'])
+ given({
+ info: {
+ license: '',
+ license_expression: null,
+ classifiers: [
+ 'License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication',
+ ],
+ },
+ }).expect(['CC0-1.0'])
+ given({
+ info: {
+ license: '',
+ license_expression: null,
+ classifiers: [
+ 'License :: OSI Approved :: GNU Affero General Public License v3',
+ ],
+ },
+ }).expect(['AGPL-3.0'])
+ given({
+ info: {
+ license: '',
+ license_expression: null,
+ classifiers: ['License :: OSI Approved :: Zero-Clause BSD (0BSD)'],
+ },
+ }).expect(['0BSD'])
})
test(getPackageFormats, () => {
given({
- info: { version: '2.19.1' },
- releases: {
- '1.0.4': [{ packagetype: 'sdist' }],
- '2.19.1': [{ packagetype: 'bdist_wheel' }, { packagetype: 'sdist' }],
- },
+ urls: [{ packagetype: 'bdist_wheel' }, { packagetype: 'sdist' }],
}).expect({ hasWheel: true, hasEgg: false })
given({
- info: { version: '1.0.4' },
- releases: {
- '1.0.4': [{ packagetype: 'sdist' }],
- '2.19.1': [{ packagetype: 'bdist_wheel' }, { packagetype: 'sdist' }],
- },
+ urls: [{ packagetype: 'sdist' }],
}).expect({ hasWheel: false, hasEgg: false })
given({
- info: { version: '0.8.2' },
- releases: {
- '0.8': [{ packagetype: 'sdist' }],
- '0.8.1': [
- { packagetype: 'bdist_egg' },
- { packagetype: 'bdist_egg' },
- { packagetype: 'sdist' },
- ],
- '0.8.2': [
- { packagetype: 'bdist_egg' },
- { packagetype: 'bdist_egg' },
- { packagetype: 'sdist' },
- ],
- },
+ urls: [
+ { packagetype: 'bdist_egg' },
+ { packagetype: 'bdist_egg' },
+ { packagetype: 'sdist' },
+ ],
}).expect({ hasWheel: false, hasEgg: true })
})
})
diff --git a/services/pypi/pypi-implementation.service.js b/services/pypi/pypi-implementation.service.js
index 1fd33a4d30789..d6ec5866fd90b 100644
--- a/services/pypi/pypi-implementation.service.js
+++ b/services/pypi/pypi-implementation.service.js
@@ -1,32 +1,23 @@
-'use strict'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { parseClassifiers } from './pypi-helpers.js'
-const PypiBase = require('./pypi-base')
-const { parseClassifiers } = require('./pypi-helpers')
+export default class PypiImplementation extends PypiBase {
+ static category = 'platform-support'
-module.exports = class PypiImplementation extends PypiBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return this.buildRoute('pypi/implementation')
- }
+ static route = this.buildRoute('pypi/implementation')
- static get examples() {
- return [
- {
- title: 'PyPI - Implementation',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ implementations: ['cpython'] }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/implementation/{packageName}': {
+ get: {
+ summary: 'PyPI Implementation',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'implementation' }
- }
+ static _cacheLength = 43200
+
+ static defaultBadgeData = { label: 'implementation' }
static render({ implementations }) {
return {
@@ -35,12 +26,12 @@ module.exports = class PypiImplementation extends PypiBase {
}
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
let implementations = parseClassifiers(
packageData,
- /^Programming Language :: Python :: Implementation :: (\S+)$/
+ /^Programming Language :: Python :: Implementation :: (\S+)$/,
)
if (implementations.length === 0) {
// Assume CPython.
diff --git a/services/pypi/pypi-implementation.tester.js b/services/pypi/pypi-implementation.tester.js
index 9a6d54fbc916f..4de094f51205c 100644
--- a/services/pypi/pypi-implementation.tester.js
+++ b/services/pypi/pypi-implementation.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('implementation (valid, package version in request)')
.get('/beehive/1.0.json')
@@ -14,9 +13,7 @@ t.create('implementation (not specified)')
.get('/chai/1.1.2.json')
.expectBadge({ label: 'implementation', message: 'cpython' })
-t.create('implementation (invalid)')
- .get('/not-a-package.json')
- .expectBadge({
- label: 'implementation',
- message: 'package or version not found',
- })
+t.create('implementation (invalid)').get('/not-a-package.json').expectBadge({
+ label: 'implementation',
+ message: 'package or version not found',
+})
diff --git a/services/pypi/pypi-license.service.js b/services/pypi/pypi-license.service.js
index cf65aa403ca44..238405429a0b3 100644
--- a/services/pypi/pypi-license.service.js
+++ b/services/pypi/pypi-license.service.js
@@ -1,36 +1,29 @@
-'use strict'
+import { renderLicenseBadge } from '../licenses.js'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { getLicenses } from './pypi-helpers.js'
-const { renderLicenseBadge } = require('../licenses')
-const PypiBase = require('./pypi-base')
-const { getLicenses } = require('./pypi-helpers')
+export default class PypiLicense extends PypiBase {
+ static category = 'license'
-module.exports = class PypiLicense extends PypiBase {
- static get category() {
- return 'license'
- }
-
- static get route() {
- return this.buildRoute('pypi/l')
- }
+ static route = this.buildRoute('pypi/l')
- static get examples() {
- return [
- {
- title: 'PyPI - License',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ licenses: ['BSD'] }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/l/{packageName}': {
+ get: {
+ summary: 'PyPI License',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
+ static _cacheLength = 43200
+
static render({ licenses }) {
return renderLicenseBadge({ licenses })
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
const licenses = getLicenses(packageData)
return this.constructor.render({ licenses })
}
diff --git a/services/pypi/pypi-license.tester.js b/services/pypi/pypi-license.tester.js
index 31c66deeac69f..1893ee30e7f51 100644
--- a/services/pypi/pypi-license.tester.js
+++ b/services/pypi/pypi-license.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('license (valid, package version in request)')
.get('/requests/2.18.4.json')
@@ -8,7 +7,7 @@ t.create('license (valid, package version in request)')
t.create('license (valid, no package version specified)')
.get('/requests.json')
- .expectBadge({ label: 'license', message: 'Apache 2.0', color: 'green' })
+ .expectBadge({ label: 'license', message: 'Apache-2.0', color: 'green' })
t.create('license (invalid)')
.get('/not-a-package.json')
@@ -25,8 +24,8 @@ t.create('license (from trove classifier)')
license: '',
classifiers: ['License :: OSI Approved :: MIT License'],
},
- releases: {},
- })
+ urls: [],
+ }),
)
.expectBadge({
label: 'license',
@@ -47,8 +46,8 @@ t.create('license (as acronym from trove classifier)')
'License :: OSI Approved :: GNU General Public License (GPL)',
],
},
- releases: {},
- })
+ urls: [],
+ }),
)
.expectBadge({
label: 'license',
diff --git a/services/pypi/pypi-python-versions.service.js b/services/pypi/pypi-python-versions.service.js
index 8a4b4168ac531..dab051a2b9661 100644
--- a/services/pypi/pypi-python-versions.service.js
+++ b/services/pypi/pypi-python-versions.service.js
@@ -1,31 +1,24 @@
-'use strict'
+import semver from 'semver'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { parseClassifiers } from './pypi-helpers.js'
-const PypiBase = require('./pypi-base')
-const { parseClassifiers } = require('./pypi-helpers')
+export default class PypiPythonVersions extends PypiBase {
+ static category = 'platform-support'
-module.exports = class PypiPythonVersions extends PypiBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return this.buildRoute('pypi/pyversions')
- }
+ static route = this.buildRoute('pypi/pyversions')
- static get examples() {
- return [
- {
- title: 'PyPI - Python Version',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ versions: ['3.5', '3.6', '3.7'] }),
+ static openApi = {
+ '/pypi/pyversions/{packageName}': {
+ get: {
+ summary: 'PyPI Python Version',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'python' }
- }
+ static _cacheLength = 21600
+
+ static defaultBadgeData = { label: 'python' }
static render({ versions }) {
const versionSet = new Set(versions)
@@ -39,7 +32,9 @@ module.exports = class PypiPythonVersions extends PypiBase {
if (versionSet.size) {
return {
message: Array.from(versionSet)
- .sort()
+ .sort((v1, v2) =>
+ semver.compare(semver.coerce(v1), semver.coerce(v2)),
+ )
.join(' | '),
color: 'blue',
}
@@ -51,20 +46,20 @@ module.exports = class PypiPythonVersions extends PypiBase {
}
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
const versions = parseClassifiers(
packageData,
- /^Programming Language :: Python :: ([\d.]+)$/
+ /^Programming Language :: Python :: ([\d.]+)$/,
)
// If no versions are found yet, check "X :: Only" as a fallback.
if (versions.length === 0) {
versions.push(
...parseClassifiers(
packageData,
- /^Programming Language :: Python :: (\d+) :: Only$/
- )
+ /^Programming Language :: Python :: (\d+) :: Only$/,
+ ),
)
}
diff --git a/services/pypi/pypi-python-versions.spec.js b/services/pypi/pypi-python-versions.spec.js
new file mode 100644
index 0000000000000..b620b5706cc8a
--- /dev/null
+++ b/services/pypi/pypi-python-versions.spec.js
@@ -0,0 +1,36 @@
+import { test, given } from 'sazerac'
+import PypiPythonVersions from './pypi-python-versions.service.js'
+
+describe('PyPI Python Version', function () {
+ test(PypiPythonVersions.render, () => {
+ // Major versions are hidden if minor are present.
+ given({ versions: ['3', '3.4', '3.5', '3.6', '2', '2.7'] }).expect({
+ message: '2.7 | 3.4 | 3.5 | 3.6',
+ color: 'blue',
+ })
+
+ // Major versions are shown when minor are missing.
+ given({ versions: ['2', '3'] }).expect({
+ message: '2 | 3',
+ color: 'blue',
+ })
+
+ // Versions are properly sorted according to their Semver segments.
+ given({ versions: ['3.10', '3.9', '3.8', '3.7', '3.6'] }).expect({
+ message: '3.6 | 3.7 | 3.8 | 3.9 | 3.10',
+ color: 'blue',
+ })
+
+ // Only "one" version works too.
+ given({ versions: ['3', '3.9'] }).expect({
+ message: '3.9',
+ color: 'blue',
+ })
+
+ // Versions are missing...
+ given({ versions: [] }).expect({
+ message: 'missing',
+ color: 'red',
+ })
+ })
+})
diff --git a/services/pypi/pypi-python-versions.tester.js b/services/pypi/pypi-python-versions.tester.js
index 25552ab4f2306..ce39ddcd426f7 100644
--- a/services/pypi/pypi-python-versions.tester.js
+++ b/services/pypi/pypi-python-versions.tester.js
@@ -1,10 +1,9 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isPipeSeparatedPythonVersions = Joi.string().regex(
- /^([0-9]+\.[0-9]+(?: \| )?)+$/
+ /^([1-9]\.[0-9]+(?: \| )?)+$/,
)
t.create('python versions (valid, package version in request)')
diff --git a/services/pypi/pypi-status.service.js b/services/pypi/pypi-status.service.js
index 0ae484a3681d1..93be17b515139 100644
--- a/services/pypi/pypi-status.service.js
+++ b/services/pypi/pypi-status.service.js
@@ -1,32 +1,23 @@
-'use strict'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { parseClassifiers } from './pypi-helpers.js'
-const PypiBase = require('./pypi-base')
-const { parseClassifiers } = require('./pypi-helpers')
+export default class PypiStatus extends PypiBase {
+ static category = 'other'
-module.exports = class PypiStatus extends PypiBase {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return this.buildRoute('pypi/status')
- }
+ static route = this.buildRoute('pypi/status')
- static get examples() {
- return [
- {
- title: 'PyPI - Status',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ status: 'stable' }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/status/{packageName}': {
+ get: {
+ summary: 'PyPI Status',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'status' }
- }
+ static _cacheLength = 43200
+
+ static defaultBadgeData = { label: 'status' }
static render({ status = '' }) {
status = status.toLowerCase()
@@ -39,6 +30,7 @@ module.exports = class PypiStatus extends PypiBase {
stable: 'brightgreen',
mature: 'brightgreen',
inactive: 'red',
+ unknown: 'lightgrey',
}[status]
return {
@@ -47,8 +39,8 @@ module.exports = class PypiStatus extends PypiBase {
}
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
// Possible statuses:
// - Development Status :: 1 - Planning
@@ -59,15 +51,19 @@ module.exports = class PypiStatus extends PypiBase {
// - Development Status :: 6 - Mature
// - Development Status :: 7 - Inactive
// https://pypi.org/pypi?%3Aaction=list_classifiers
- const status = parseClassifiers(
+ let status = parseClassifiers(
packageData,
- /^Development Status :: (\d - \S+)$/
+ /^Development Status :: (\d - \S+)$/,
)
.sort()
.map(classifier => classifier.split(' - ').pop())
.map(classifier => classifier.replace(/production\/stable/i, 'stable'))
.pop()
+ if (!status) {
+ status = 'Unknown'
+ }
+
return this.constructor.render({ status })
}
}
diff --git a/services/pypi/pypi-status.tester.js b/services/pypi/pypi-status.tester.js
index 751da746de7b0..e2f32bb8e94b3 100644
--- a/services/pypi/pypi-status.tester.js
+++ b/services/pypi/pypi-status.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('status (valid, stable, package version in request)')
.get('/django/1.11.json')
@@ -14,6 +13,10 @@ t.create('status (valid, beta)')
.get('/django/2.0rc1.json')
.expectBadge({ label: 'status', message: 'beta' })
+t.create('status (status not specified)')
+ .get('/arcgis2geojson/3.0.2.json')
+ .expectBadge({ label: 'status', message: 'unknown' })
+
t.create('status (invalid)')
.get('/not-a-package.json')
.expectBadge({ label: 'status', message: 'package or version not found' })
diff --git a/services/pypi/pypi-types.service.js b/services/pypi/pypi-types.service.js
new file mode 100644
index 0000000000000..5329200870c8e
--- /dev/null
+++ b/services/pypi/pypi-types.service.js
@@ -0,0 +1,50 @@
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+
+export default class PypiTypes extends PypiBase {
+ static category = 'platform-support'
+
+ static route = this.buildRoute('pypi/types')
+
+ static openApi = {
+ '/pypi/types/{packageName}': {
+ get: {
+ summary: 'PyPI Types',
+ description:
+ 'Type information provided by the package, as indicated by the presence of the `Typing :: Typed` and `Typing :: Stubs Only` classifiers in the package metadata',
+ parameters: pypiGeneralParams,
+ },
+ },
+ }
+
+ static _cacheLength = 43200
+
+ static defaultBadgeData = { label: 'types' }
+
+ static render({ isTyped, isStubsOnly }) {
+ if (isTyped) {
+ return {
+ message: 'typed',
+ color: 'brightgreen',
+ }
+ } else if (isStubsOnly) {
+ return {
+ message: 'stubs',
+ color: 'brightgreen',
+ }
+ } else {
+ return {
+ message: 'untyped',
+ color: 'red',
+ }
+ }
+ }
+
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
+ const isTyped = packageData.info.classifiers.includes('Typing :: Typed')
+ const isStubsOnly = packageData.info.classifiers.includes(
+ 'Typing :: Stubs Only',
+ )
+ return this.constructor.render({ isTyped, isStubsOnly })
+ }
+}
diff --git a/services/pypi/pypi-types.tester.js b/services/pypi/pypi-types.tester.js
new file mode 100644
index 0000000000000..450feea729916
--- /dev/null
+++ b/services/pypi/pypi-types.tester.js
@@ -0,0 +1,18 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('types (yes)')
+ .get('/pyre-check.json')
+ .expectBadge({ label: 'types', message: 'typed' })
+
+t.create('types (no)')
+ .get('/z3-solver.json')
+ .expectBadge({ label: 'types', message: 'untyped' })
+
+t.create('types (stubs)')
+ .get('/types-requests.json')
+ .expectBadge({ label: 'types', message: 'stubs' })
+
+t.create('types (invalid)')
+ .get('/not-a-package.json')
+ .expectBadge({ label: 'types', message: 'package or version not found' })
diff --git a/services/pypi/pypi-version.service.js b/services/pypi/pypi-version.service.js
index 271adfe6ebfb9..0630bc3cadfe2 100644
--- a/services/pypi/pypi-version.service.js
+++ b/services/pypi/pypi-version.service.js
@@ -1,41 +1,33 @@
-'use strict'
+import { pep440VersionColor } from '../color-formatters.js'
+import { renderVersionBadge } from '../version.js'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
-const { renderVersionBadge } = require('../version')
-const PypiBase = require('./pypi-base')
+export default class PypiVersion extends PypiBase {
+ static category = 'version'
-module.exports = class PypiVersion extends PypiBase {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return this.buildRoute('pypi/v')
- }
+ static route = this.buildRoute('pypi/v')
- static get examples() {
- return [
- {
- title: 'PyPI',
- pattern: ':packageName',
- namedParams: { packageName: 'nine' },
- staticPreview: this.render({ version: '1.0.0' }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/v/{packageName}': {
+ get: {
+ summary: 'PyPI Version',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'pypi' }
- }
+ static _cacheLength = 10800
+
+ static defaultBadgeData = { label: 'pypi' }
static render({ version }) {
- return renderVersionBadge({ version })
+ return renderVersionBadge({ version, versionFormatter: pep440VersionColor })
}
- async handle({ egg }) {
+ async handle({ egg }, { pypiBaseUrl }) {
const {
info: { version },
- } = await this.fetch({ egg })
+ } = await this.fetch({ egg, pypiBaseUrl })
return this.constructor.render({ version })
}
}
diff --git a/services/pypi/pypi-version.tester.js b/services/pypi/pypi-version.tester.js
index 9e0fc375754f8..17059084dcb86 100644
--- a/services/pypi/pypi-version.tester.js
+++ b/services/pypi/pypi-version.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { isSemver } = require('../test-validators')
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { isSemver } from '../test-validators.js'
+export const t = await createServiceTester()
const isPsycopg2Version = Joi.string().regex(/^v([0-9][.]?)+$/)
@@ -18,20 +17,16 @@ const isPsycopg2Version = Joi.string().regex(/^v([0-9][.]?)+$/)
We'll run this test against a project that follows SemVer...
*/
-t.create('version (semver)')
- .get('/requests.json')
- .expectBadge({
- label: 'pypi',
- message: isSemver,
- })
+t.create('version (semver)').get('/requests.json').expectBadge({
+ label: 'pypi',
+ message: isSemver,
+})
-// ..whereas this project does not folow SemVer
-t.create('version (not semver)')
- .get('/psycopg2.json')
- .expectBadge({
- label: 'pypi',
- message: isPsycopg2Version,
- })
+// ..whereas this project does not follow SemVer
+t.create('version (not semver)').get('/psycopg2.json').expectBadge({
+ label: 'pypi',
+ message: isPsycopg2Version,
+})
t.create('version (invalid)')
.get('/not-a-package.json')
@@ -48,8 +43,8 @@ t.create('no trove classifiers')
license: 'foo',
classifiers: [],
},
- releases: {},
- })
+ urls: [],
+ }),
)
.expectBadge({
label: 'pypi',
diff --git a/services/pypi/pypi-wheel.service.js b/services/pypi/pypi-wheel.service.js
index 91a5b448aed29..e1da74933108a 100644
--- a/services/pypi/pypi-wheel.service.js
+++ b/services/pypi/pypi-wheel.service.js
@@ -1,32 +1,23 @@
-'use strict'
+import PypiBase, { pypiGeneralParams } from './pypi-base.js'
+import { getPackageFormats } from './pypi-helpers.js'
-const PypiBase = require('./pypi-base')
-const { getPackageFormats } = require('./pypi-helpers')
+export default class PypiWheel extends PypiBase {
+ static category = 'platform-support'
-module.exports = class PypiWheel extends PypiBase {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return this.buildRoute('pypi/wheel')
- }
+ static route = this.buildRoute('pypi/wheel')
- static get examples() {
- return [
- {
- title: 'PyPI - Wheel',
- pattern: ':packageName',
- namedParams: { packageName: 'Django' },
- staticPreview: this.render({ hasWheel: true }),
- keywords: ['python'],
+ static openApi = {
+ '/pypi/wheel/{packageName}': {
+ get: {
+ summary: 'PyPI Wheel',
+ parameters: pypiGeneralParams,
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'wheel' }
- }
+ static _cacheLength = 43200
+
+ static defaultBadgeData = { label: 'wheel' }
static render({ hasWheel }) {
if (hasWheel) {
@@ -42,8 +33,8 @@ module.exports = class PypiWheel extends PypiBase {
}
}
- async handle({ egg }) {
- const packageData = await this.fetch({ egg })
+ async handle({ egg }, { pypiBaseUrl }) {
+ const packageData = await this.fetch({ egg, pypiBaseUrl })
const { hasWheel } = getPackageFormats(packageData)
return this.constructor.render({ hasWheel })
}
diff --git a/services/pypi/pypi-wheel.tester.js b/services/pypi/pypi-wheel.tester.js
index d888b6a05df2d..a70afaf4d4bf5 100644
--- a/services/pypi/pypi-wheel.tester.js
+++ b/services/pypi/pypi-wheel.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('wheel (has wheel, package version in request)')
.get('/requests/2.18.4.json')
diff --git a/services/python/python-version-from-toml.service.js b/services/python/python-version-from-toml.service.js
new file mode 100644
index 0000000000000..9f6b06b43ada1
--- /dev/null
+++ b/services/python/python-version-from-toml.service.js
@@ -0,0 +1,68 @@
+import Joi from 'joi'
+import BaseTomlService from '../../core/base-service/base-toml.js'
+import { queryParams } from '../index.js'
+import { url } from '../validators.js'
+
+const queryParamSchema = Joi.object({
+ tomlFilePath: url,
+}).required()
+
+const schema = Joi.object({
+ project: Joi.object({
+ 'requires-python': Joi.string().required(),
+ }).required(),
+}).required()
+
+const description = `Shows the required python version for a package based on the values in the requires-python field in PEP 621 compliant pyproject.toml \n
+a URL of the toml is required, please note that when linking to files in github or similar sites, provide URL to raw file, for example:
+
+Use https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml \n
+And not https://github.com/numpy/numpy/blob/main/pyproject.toml
+`
+
+class PythonVersionFromToml extends BaseTomlService {
+ static category = 'platform-support'
+
+ static route = {
+ base: '',
+ pattern: 'python/required-version-toml',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/python/required-version-toml': {
+ get: {
+ summary: 'Python Version from PEP 621 TOML',
+ description,
+ parameters: queryParams({
+ name: 'tomlFilePath',
+ example:
+ 'https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml',
+ required: true,
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'python' }
+
+ static render({ requiresPythonString }) {
+ // we only show requries-python as is
+ // for more info read the following issues:
+ // https://github.com/badges/shields/issues/9410
+ // https://github.com/badges/shields/issues/5551
+ return {
+ message: requiresPythonString,
+ color: 'blue',
+ }
+ }
+
+ async handle(namedParams, { tomlFilePath }) {
+ const tomlData = await this._requestToml({ url: tomlFilePath, schema })
+ const requiresPythonString = tomlData.project['requires-python']
+
+ return this.constructor.render({ requiresPythonString })
+ }
+}
+
+export { PythonVersionFromToml }
diff --git a/services/python/python-version-from-toml.tester.js b/services/python/python-version-from-toml.tester.js
new file mode 100644
index 0000000000000..db74e5769b4e7
--- /dev/null
+++ b/services/python/python-version-from-toml.tester.js
@@ -0,0 +1,27 @@
+import Joi from 'joi'
+import { validRange } from '@renovatebot/pep440'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+const validatePep440 = (value, helpers) => {
+ if (!validRange(value)) {
+ return helpers.error('any.invalid')
+ }
+ return value
+}
+
+const isCommaSeparatedPythonVersions = Joi.string().custom(validatePep440)
+
+t.create('python versions (valid)')
+ .get(
+ '/python/required-version-toml.json?tomlFilePath=https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml',
+ )
+ .expectBadge({ label: 'python', message: isCommaSeparatedPythonVersions })
+
+t.create(
+ 'python versions - valid toml with missing python-requires field (invalid)',
+)
+ .get(
+ '/python/required-version-toml.json?tomlFilePath=https://raw.githubusercontent.com/psf/requests/v2.31.0/pyproject.toml',
+ )
+ .expectBadge({ label: 'python', message: 'invalid response data' })
diff --git a/services/raycast/installs.service.js b/services/raycast/installs.service.js
new file mode 100644
index 0000000000000..9bbde6bcf4c83
--- /dev/null
+++ b/services/raycast/installs.service.js
@@ -0,0 +1,55 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+
+const schema = Joi.object({
+ download_count: nonNegativeInteger,
+}).required()
+
+export default class RaycastInstalls extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'raycast/dt',
+ pattern: ':user/:extension',
+ }
+
+ static openApi = {
+ '/raycast/dt/{user}/{extension}': {
+ get: {
+ summary: 'Raycast extension downloads count',
+ parameters: pathParams(
+ { name: 'user', example: 'Fatpandac' },
+ { name: 'extension', example: 'bilibili' },
+ ),
+ },
+ },
+ }
+
+ static render({ downloads }) {
+ return renderDownloadsBadge({ downloads })
+ }
+
+ async fetch({ user, extension }) {
+ return this._requestJson({
+ schema,
+ url: `https://www.raycast.com/api/v1/extensions/${user}/${extension}`,
+ httpErrors: {
+ 404: 'user/extension not found',
+ },
+ })
+ }
+
+ transform(json) {
+ const downloads = json.download_count
+
+ return { downloads }
+ }
+
+ async handle({ user, extension }) {
+ const json = await this.fetch({ user, extension })
+ const { downloads } = this.transform(json)
+ return this.constructor.render({ downloads })
+ }
+}
diff --git a/services/raycast/installs.tester.js b/services/raycast/installs.tester.js
new file mode 100644
index 0000000000000..e6e8e957f0493
--- /dev/null
+++ b/services/raycast/installs.tester.js
@@ -0,0 +1,30 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('installs (invalid user)')
+ .get('/fatpandac/bilibili.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'user/extension not found',
+ })
+
+t.create('installs (not existing extension)')
+ .get('/Fatpandac/safdsaklfhe.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'user/extension not found',
+ })
+
+t.create('installs (not existing user and extension)')
+ .get('/fatpandac/safdsaklfhe.json')
+ .expectBadge({
+ label: 'downloads',
+ message: 'user/extension not found',
+ })
+
+t.create('installs (valid)').get('/Fatpandac/bilibili.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
diff --git a/services/readthedocs/readthedocs.service.js b/services/readthedocs/readthedocs.service.js
index 18ae244e6432b..d796959076b94 100644
--- a/services/readthedocs/readthedocs.service.js
+++ b/services/readthedocs/readthedocs.service.js
@@ -1,71 +1,104 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService, NotFound } = require('..')
-
-const keywords = ['documentation']
+import Joi from 'joi'
+import { renderBuildStatusBadge } from '../build-status.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
const schema = Joi.object({
- message: Joi.alternatives()
- .try(isBuildStatus, Joi.equal('unknown'))
+ results: Joi.array()
+ .items(
+ Joi.object({
+ state: Joi.object({
+ code: Joi.string().required(),
+ }).required(),
+ success: Joi.boolean().required(),
+ }),
+ )
.required(),
}).required()
-module.exports = class ReadTheDocs extends BaseSvgScrapingService {
- static get category() {
- return 'build'
+const description =
+ '[ReadTheDocs](https://readthedocs.com/) is a hosting service for documentation.'
+
+export default class ReadTheDocs extends BaseJsonService {
+ static category = 'build'
+
+ static route = {
+ base: 'readthedocs',
+ pattern: ':project/:version?',
}
- static get route() {
- return {
- base: 'readthedocs',
- pattern: ':project/:version?',
- }
+ static auth = {
+ passKey: 'readthedocs_token',
+ authorizedOrigins: ['https://app.readthedocs.org'],
}
- static get examples() {
- return [
- {
- title: 'Read the Docs',
- pattern: ':packageName',
- namedParams: { packageName: 'pip' },
- staticPreview: this.render({ status: 'passing' }),
- keywords,
+ static openApi = {
+ '/readthedocs/{packageName}': {
+ get: {
+ summary: 'Read the Docs',
+ description,
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'pip',
+ }),
},
- {
- title: 'Read the Docs (version)',
- pattern: ':packageName/:version',
- namedParams: { packageName: 'pip', version: 'stable' },
- staticPreview: this.render({ status: 'passing' }),
- keywords,
+ },
+ '/readthedocs/{packageName}/{version}': {
+ get: {
+ summary: 'Read the Docs (version)',
+ description,
+ parameters: pathParams(
+ {
+ name: 'packageName',
+ example: 'pip',
+ },
+ {
+ name: 'version',
+ example: 'stable',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'docs',
- }
+ static _cacheLength = 300
+
+ static defaultBadgeData = {
+ label: 'docs',
}
static render({ status }) {
return renderBuildStatusBadge({ status })
}
+ async fetch({ project, version = 'latest' }) {
+ return this._requestJson(
+ this.authHelper.withBearerAuthHeader(
+ {
+ schema,
+ url: `https://app.readthedocs.org/api/v3/projects/${encodeURIComponent(project)}/versions/${encodeURIComponent(version)}/builds/`,
+ options: {
+ searchParams: {
+ fields: 'state,success',
+ limit: 10,
+ running: false,
+ },
+ },
+ },
+ 'Token',
+ ),
+ )
+ }
+
async handle({ project, version }) {
- const { message: status } = await this._requestSvg({
- schema,
- url: `https://readthedocs.org/projects/${encodeURIComponent(
- project
- )}/badge/`,
- options: { qs: { version } },
- })
- if (status === 'unknown') {
+ const { results } = await this.fetch({ project, version })
+ const build = results.find(({ state }) => state.code === 'finished')
+ if (!build) {
throw new NotFound({
- prettyMessage: 'project or version not found',
+ prettyMessage: 'no finished builds',
})
}
- return this.constructor.render({ status })
+ return this.constructor.render({
+ status: build.success ? 'passing' : 'failing',
+ })
}
}
diff --git a/services/readthedocs/readthedocs.spec.js b/services/readthedocs/readthedocs.spec.js
new file mode 100644
index 0000000000000..f765c39fecea2
--- /dev/null
+++ b/services/readthedocs/readthedocs.spec.js
@@ -0,0 +1,25 @@
+import { testAuth } from '../test-helpers.js'
+import ReadTheDocs from './readthedocs.service.js'
+
+describe('ReadTheDocs', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ ReadTheDocs,
+ 'BearerAuthHeader',
+ {
+ results: [
+ {
+ state: { code: 'finished' },
+ success: true,
+ },
+ ],
+ },
+ {
+ bearerHeaderKey: 'Token',
+ exampleOverride: { project: 'pip' },
+ },
+ )
+ })
+ })
+})
diff --git a/services/readthedocs/readthedocs.tester.js b/services/readthedocs/readthedocs.tester.js
index eab0e3e4b4a85..7321ab88d60c2 100644
--- a/services/readthedocs/readthedocs.tester.js
+++ b/services/readthedocs/readthedocs.tester.js
@@ -1,38 +1,74 @@
-'use strict'
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = await createServiceTester()
-t.create('build status')
+const apiBaseUrl = 'https://app.readthedocs.org'
+const buildsQuery = {
+ fields: 'state,success',
+ limit: 10,
+ running: false,
+}
+
+const passingBuild = {
+ state: { code: 'finished' },
+ success: true,
+}
+
+const failingBuild = {
+ state: { code: 'finished' },
+ success: false,
+}
+
+t.create('defaults to the latest version')
.get('/pip.json')
- .expectBadge({
- label: 'docs',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
+ .expectBadge({ label: 'docs', message: Joi.equal('passing', 'failing') })
-t.create('build status for named version')
+t.create('passing build for a named version')
.get('/pip/stable.json')
- .expectBadge({
- label: 'docs',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
+ .intercept(nock =>
+ nock(apiBaseUrl)
+ .get('/api/v3/projects/pip/versions/stable/builds/')
+ .query(buildsQuery)
+ .reply(200, { results: [passingBuild] }),
+ )
+ .expectBadge({ label: 'docs', message: 'passing' })
-t.create('build status for named semantic version')
+t.create('failing build for a named version')
.get('/scrapy/1.0.json')
- .expectBadge({
- label: 'docs',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
-
-t.create('build status for nonexistent version')
- // This establishes that the version is being sent.
- .get('/pip/foobar-is-not-a-version.json')
- .expectBadge({
- label: 'docs',
- message: 'project or version not found',
- })
+ .intercept(nock =>
+ nock(apiBaseUrl)
+ .get('/api/v3/projects/scrapy/versions/1.0/builds/')
+ .query(buildsQuery)
+ .reply(200, { results: [failingBuild] }),
+ )
+ .expectBadge({ label: 'docs', message: 'failing' })
+
+t.create('uses the latest finished build')
+ .get('/pip/latest.json')
+ .intercept(nock =>
+ nock(apiBaseUrl)
+ .get('/api/v3/projects/pip/versions/latest/builds/')
+ .query(buildsQuery)
+ .reply(200, {
+ results: [
+ { state: { code: 'cancelled' }, success: false },
+ passingBuild,
+ ],
+ }),
+ )
+ .expectBadge({ label: 'docs', message: 'passing' })
+
+t.create('no finished builds')
+ .get('/pip/unbuilt.json')
+ .intercept(nock =>
+ nock(apiBaseUrl)
+ .get('/api/v3/projects/pip/versions/unbuilt/builds/')
+ .query(buildsQuery)
+ .reply(200, { results: [] }),
+ )
+ .expectBadge({ label: 'docs', message: 'no finished builds' })
t.create('unknown project')
.get('/this-repo/does-not-exist.json')
- .expectBadge({ label: 'docs', message: 'project or version not found' })
+ .expectBadge({ label: 'docs', message: 'no finished builds' })
diff --git a/services/reddit/reddit-base.js b/services/reddit/reddit-base.js
new file mode 100644
index 0000000000000..992f2f4dcbd0b
--- /dev/null
+++ b/services/reddit/reddit-base.js
@@ -0,0 +1,89 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+
+const tokenSchema = Joi.object({
+ access_token: Joi.string().required(),
+ expires_in: Joi.number(),
+})
+
+// Abstract class for Reddit badges
+// Authorization flow based on https://github.com/reddit-archive/reddit/wiki/OAuth2#application-only-oauth.
+export default class RedditBase extends BaseJsonService {
+ static category = 'social'
+
+ static auth = {
+ userKey: 'reddit_client_id',
+ passKey: 'reddit_client_secret',
+ authorizedOrigins: ['https://www.reddit.com'],
+ isRequired: false,
+ }
+
+ constructor(...args) {
+ super(...args)
+ if (!RedditBase._redditToken && this.authHelper.isConfigured) {
+ RedditBase._redditToken = this._getNewToken()
+ }
+ }
+
+ async _getNewToken() {
+ const tokenRes = await super._requestJson(
+ this.authHelper.withBasicAuth({
+ schema: tokenSchema,
+ url: 'https://www.reddit.com/api/v1/access_token',
+ options: {
+ method: 'POST',
+ body: 'grant_type=client_credentials',
+ },
+ httpErrors: {
+ 401: 'invalid token',
+ },
+ }),
+ )
+
+ // replace the token when we are 80% near the expire time
+ // 2147483647 is the max 32-bit value that is accepted by setTimeout(), it's about 24.9 days
+ const replaceTokenMs = Math.min(
+ tokenRes.expires_in * 1000 * 0.8,
+ 2147483647,
+ )
+ const timeout = setTimeout(() => {
+ RedditBase._redditToken = this._getNewToken()
+ }, replaceTokenMs)
+
+ // do not block program exit
+ timeout.unref()
+
+ return tokenRes.access_token
+ }
+
+ async _requestJson(request) {
+ if (!this.authHelper.isConfigured) {
+ return super._requestJson(request)
+ }
+
+ request = await this.addBearerAuthHeader(request)
+ try {
+ return await super._requestJson(request)
+ } catch (err) {
+ if (err.response && err.response.statusCode === 401) {
+ // if the token is expired or has been revoked, retry once
+ RedditBase._redditToken = this._getNewToken()
+ request = await this.addBearerAuthHeader(request)
+ return super._requestJson(request)
+ }
+ // cannot recover
+ throw err
+ }
+ }
+
+ async addBearerAuthHeader(request) {
+ return {
+ ...request,
+ options: {
+ headers: {
+ Authorization: `Bearer ${await RedditBase._redditToken}`,
+ },
+ },
+ }
+ }
+}
diff --git a/services/reddit/subreddit-subscribers.service.js b/services/reddit/subreddit-subscribers.service.js
index e59273f478ee2..2a505536b81f6 100644
--- a/services/reddit/subreddit-subscribers.service.js
+++ b/services/reddit/subreddit-subscribers.service.js
@@ -1,9 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalNonNegativeInteger } = require('../validators')
-const { metric } = require('../text-formatters')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { optionalNonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import RedditBase from './reddit-base.js'
const schema = Joi.object({
data: Joi.object({
@@ -11,44 +10,34 @@ const schema = Joi.object({
}).required(),
}).required()
-module.exports = class SubredditSubscribers extends BaseJsonService {
- static get category() {
- return 'social'
+export default class RedditSubredditSubscribers extends RedditBase {
+ static route = {
+ base: 'reddit/subreddit-subscribers',
+ pattern: ':subreddit',
}
- static get route() {
- return {
- base: 'reddit/subreddit-subscribers',
- pattern: ':subreddit',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Subreddit subscribers',
- namedParams: { subreddit: 'drums' },
- staticPreview: {
- label: 'follow r/drums',
- message: '77k',
- color: 'red',
- style: 'social',
- },
+ static openApi = {
+ '/reddit/subreddit-subscribers/{subreddit}': {
+ get: {
+ summary: 'Subreddit subscribers',
+ parameters: pathParams({
+ name: 'subreddit',
+ example: 'drums',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'reddit',
- namedLogo: 'reddit',
- }
+ static defaultBadgeData = {
+ label: 'reddit',
+ namedLogo: 'reddit',
}
static render({ subreddit, subscribers }) {
return {
label: `follow r/${subreddit}`,
message: metric(subscribers),
+ style: 'social',
color: 'red',
link: [`https://www.reddit.com/r/${subreddit}`],
}
@@ -57,9 +46,13 @@ module.exports = class SubredditSubscribers extends BaseJsonService {
async fetch({ subreddit }) {
return this._requestJson({
schema,
- url: `https://www.reddit.com/r/${subreddit}/about.json`,
- errorMessages: {
+ // API requests with a bearer token should be made to https://oauth.reddit.com, NOT www.reddit.com.
+ url: this.authHelper.isConfigured
+ ? `https://oauth.reddit.com/r/${subreddit}/about.json`
+ : `https://www.reddit.com/r/${subreddit}/about.json`,
+ httpErrors: {
404: 'subreddit not found',
+ 403: 'subreddit is private',
},
})
}
diff --git a/services/reddit/subreddit-subscribers.tester.js b/services/reddit/subreddit-subscribers.tester.js
index 9e4d12e3c71b4..b993551c02841 100644
--- a/services/reddit/subreddit-subscribers.tester.js
+++ b/services/reddit/subreddit-subscribers.tester.js
@@ -1,7 +1,10 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import _serviceClass from './subreddit-subscribers.service.js'
+export const t = await createServiceTester()
+const noRedditToken = noToken(_serviceClass)
+const hasRedditToken = () => !noRedditToken()
t.create('subreddit-subscribers (valid subreddit)')
.get('/drums.json')
@@ -23,3 +26,37 @@ t.create('subreddit-subscribers (not existing subreddit)')
label: 'reddit',
message: 'subreddit not found',
})
+
+t.create('subreddit-subscribers (private sub)')
+ .get('/centuryclub.json')
+ .expectBadge({
+ label: 'reddit',
+ message: 'subreddit is private',
+ })
+
+t.create('subreddit-subscribers (private sub, without token)')
+ .skipWhen(hasRedditToken)
+ .get('/centuryclub.json')
+ .intercept(nock =>
+ nock('https://www.reddit.com/r')
+ .get('/centuryclub/about.json')
+ .reply(200, { kind: 't5', data: {} }),
+ )
+ .expectBadge({
+ label: 'reddit',
+ message: 'subreddit not found',
+ })
+
+t.create('subreddit-subscribers (private sub, with token)')
+ .skipWhen(noRedditToken)
+ .get('/centuryclub.json')
+ .intercept(nock =>
+ nock('https://oauth.reddit.com/r')
+ .get('/centuryclub/about.json')
+ .reply(200, { kind: 't5', data: {} }),
+ )
+ .networkOn() // API /access_token may or may not be called depending on whether another test ran before and cached the token. Rather than conditionally intercepting it, let it go through and only mock the API call we're validating specific behaviour against.
+ .expectBadge({
+ label: 'reddit',
+ message: 'subreddit not found',
+ })
diff --git a/services/reddit/user-karma.service.js b/services/reddit/user-karma.service.js
new file mode 100644
index 0000000000000..460ae2d771d88
--- /dev/null
+++ b/services/reddit/user-karma.service.js
@@ -0,0 +1,95 @@
+import Joi from 'joi'
+import { anyInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import { pathParams } from '../index.js'
+import RedditBase from './reddit-base.js'
+
+const schema = Joi.object({
+ data: Joi.object({
+ link_karma: anyInteger,
+ comment_karma: anyInteger,
+ }).required(),
+}).required()
+
+export default class RedditUserKarma extends RedditBase {
+ static route = {
+ base: 'reddit/user-karma',
+ pattern: ':variant(link|comment|combined)/:user',
+ }
+
+ static openApi = {
+ '/reddit/user-karma/{variant}/{user}': {
+ get: {
+ summary: 'Reddit User Karma',
+ parameters: pathParams(
+ {
+ name: 'variant',
+ example: 'combined',
+ schema: { type: 'string', enum: this.getEnum('variant') },
+ },
+ {
+ name: 'user',
+ example: 'example',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'reddit karma',
+ namedLogo: 'reddit',
+ }
+
+ static render({ variant, karma, user }) {
+ const label =
+ variant === 'combined'
+ ? `u/${user} karma`
+ : `u/${user} karma (${variant})`
+ return {
+ label,
+ message: metric(karma),
+ style: 'social',
+ color: karma > 0 ? 'brightgreen' : karma === 0 ? 'orange' : 'red',
+ link: [`https://www.reddit.com/u/${user}`],
+ }
+ }
+
+ async fetch({ user }) {
+ return this._requestJson({
+ schema,
+ // API requests with a bearer token should be made to https://oauth.reddit.com, NOT www.reddit.com.
+ url: this.authHelper.isConfigured
+ ? `https://oauth.reddit.com/u/${user}/about.json`
+ : `https://www.reddit.com/u/${user}/about.json`,
+ httpErrors: {
+ 404: 'user not found',
+ },
+ })
+ }
+
+ transform({ json, variant }) {
+ let karma
+ if (variant === 'link') {
+ karma = json.data.link_karma
+ } else if (variant === 'comment') {
+ karma = json.data.comment_karma
+ } else {
+ const total = json.data.link_karma + json.data.comment_karma
+ karma = total
+ }
+
+ return { karma }
+ }
+
+ async handle({ variant, user }) {
+ const json = await this.fetch({ user })
+ const { karma } = this.transform({ json, variant })
+
+ return this.constructor.render({
+ variant,
+ karma,
+ user,
+ })
+ }
+}
diff --git a/services/reddit/user-karma.tester.js b/services/reddit/user-karma.tester.js
new file mode 100644
index 0000000000000..418d1533f2678
--- /dev/null
+++ b/services/reddit/user-karma.tester.js
@@ -0,0 +1,143 @@
+import { noToken } from '../test-helpers.js'
+import { isMetricAllowNegative } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import _serviceClass from './subreddit-subscribers.service.js'
+export const t = await createServiceTester()
+const noRedditToken = noToken(_serviceClass)
+const hasRedditToken = () => !noRedditToken()
+
+t.create('user-karma (valid - link)')
+ .get('/link/user_simulator.json')
+ .expectBadge({
+ label: 'u/user_simulator karma (link)',
+ message: isMetricAllowNegative,
+ })
+
+t.create('user-karma (valid - comment')
+ .get('/comment/user_simulator.json')
+ .expectBadge({
+ label: 'u/user_simulator karma (comment)',
+ message: isMetricAllowNegative,
+ })
+
+t.create('user-karma (valid - combined)')
+ .get('/combined/user_simulator.json')
+ .expectBadge({
+ label: 'u/user_simulator karma',
+ message: isMetricAllowNegative,
+ })
+
+t.create('user-karma (non-existing user)')
+ .get('/combined/thisuserdoesnotexistandhopefullyneverwill.json')
+ .expectBadge({
+ label: 'reddit karma',
+ message: 'user not found',
+ })
+
+t.create('user-karma (link - math check, without token)')
+ .skipWhen(hasRedditToken)
+ .get('/link/user_simulator.json')
+ .intercept(nock =>
+ nock('https://www.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .expectBadge({
+ label: 'u/user_simulator karma (link)',
+ message: '20',
+ })
+
+t.create('user-karma (link - math check, with token)')
+ .skipWhen(noRedditToken)
+ .get('/link/user_simulator.json')
+ .intercept(nock =>
+ nock('https://oauth.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .networkOn() // API /access_token may or may not be called depending on whether another test ran before and cached the token. Rather than conditionally intercepting it, let it go through and only mock the API call we're validating specific behaviour against.
+ .expectBadge({
+ label: 'u/user_simulator karma (link)',
+ message: '20',
+ })
+
+t.create('user-karma (comment - math check, without token)')
+ .skipWhen(hasRedditToken)
+ .get('/comment/user_simulator.json')
+ .intercept(nock =>
+ nock('https://www.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .expectBadge({
+ label: 'u/user_simulator karma (comment)',
+ message: '80',
+ })
+
+t.create('user-karma (comment - math check, with token)')
+ .skipWhen(noRedditToken)
+ .get('/comment/user_simulator.json')
+ .intercept(nock =>
+ nock('https://oauth.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .networkOn() // API /access_token may or may not be called depending on whether another test ran before and cached the token. Rather than conditionally intercepting it, let it go through and only mock the API call we're validating specific behaviour against.
+ .expectBadge({
+ label: 'u/user_simulator karma (comment)',
+ message: '80',
+ })
+
+t.create('user-karma (combined - math check, without token)')
+ .skipWhen(hasRedditToken)
+ .get('/combined/user_simulator.json')
+ .intercept(nock =>
+ nock('https://www.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .expectBadge({
+ label: 'u/user_simulator karma',
+ message: '100',
+ })
+
+t.create('user-karma (combined - math check, with token)')
+ .skipWhen(noRedditToken)
+ .get('/combined/user_simulator.json')
+ .intercept(nock =>
+ nock('https://oauth.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20, comment_karma: 80 } }),
+ )
+ .networkOn() // API /access_token may or may not be called depending on whether another test ran before and cached the token. Rather than conditionally intercepting it, let it go through and only mock the API call we're validating specific behaviour against.
+ .expectBadge({
+ label: 'u/user_simulator karma',
+ message: '100',
+ })
+
+t.create('user-karma (combined - missing data, without token)')
+ .skipWhen(hasRedditToken)
+ .get('/combined/user_simulator.json')
+ .intercept(nock =>
+ nock('https://www.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20 } }),
+ )
+ .expectBadge({
+ label: 'reddit karma',
+ message: 'invalid response data',
+ })
+
+t.create('user-karma (combined - missing data, with token)')
+ .skipWhen(noRedditToken)
+ .get('/combined/user_simulator.json')
+ .intercept(nock =>
+ nock('https://oauth.reddit.com/u')
+ .get('/user_simulator/about.json')
+ .reply(200, { kind: 't2', data: { link_karma: 20 } }),
+ )
+ .networkOn() // API /access_token may or may not be called depending on whether another test ran before and cached the token. Rather than conditionally intercepting it, let it go through and only mock the API call we're validating specific behaviour against.
+ .expectBadge({
+ label: 'reddit karma',
+ message: 'invalid response data',
+ })
diff --git a/services/redmine/redmine.service.js b/services/redmine/redmine.service.js
deleted file mode 100644
index b4a95e97e656a..0000000000000
--- a/services/redmine/redmine.service.js
+++ /dev/null
@@ -1,95 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { starRating } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseXmlService } = require('..')
-
-const schema = Joi.object({
- 'redmine-plugin': Joi.object({
- 'ratings-average': Joi.number()
- .min(0)
- .required(),
- }).required(),
-})
-
-class BaseRedminePluginRating extends BaseXmlService {
- static get category() {
- return 'rating'
- }
-
- static render({ rating }) {
- throw new Error(`render() function not implemented for ${this.name}`)
- }
-
- async fetch({ plugin }) {
- const url = `https://www.redmine.org/plugins/${plugin}.xml`
- return this._requestXml({ schema, url })
- }
-
- async handle({ plugin }) {
- const data = await this.fetch({ plugin })
- const rating = data['redmine-plugin']['ratings-average']
- return this.constructor.render({ rating })
- }
-}
-
-class RedminePluginRating extends BaseRedminePluginRating {
- static get route() {
- return {
- base: 'redmine/plugin/rating',
- pattern: ':plugin',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Plugin on redmine.org',
- namedParams: { plugin: 'redmine_xlsx_format_issue_exporter' },
- staticPreview: this.render({ rating: 5 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'redmine' }
- }
-
- static render({ rating }) {
- return {
- label: 'rating',
- message: `${rating.toFixed(1)}/5.0`,
- color: floorCountColor(rating, 2, 3, 4),
- }
- }
-}
-
-class RedminePluginStars extends BaseRedminePluginRating {
- static get route() {
- return {
- base: 'redmine/plugin/stars',
- pattern: ':plugin',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Plugin on redmine.org',
- namedParams: { plugin: 'redmine_xlsx_format_issue_exporter' },
- staticPreview: this.render({ rating: 5 }),
- },
- ]
- }
-
- static render({ rating }) {
- return {
- label: 'stars',
- message: starRating(Math.round(rating)),
- color: floorCountColor(rating, 2, 3, 4),
- }
- }
-}
-
-module.exports = { RedminePluginRating, RedminePluginStars }
diff --git a/services/redmine/redmine.tester.js b/services/redmine/redmine.tester.js
deleted file mode 100644
index f83d51049c6cb..0000000000000
--- a/services/redmine/redmine.tester.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { isStarRating } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
- id: 'redmine',
- title: 'Redmine',
-}))
-
-t.create('plugin rating')
- .get('/plugin/rating/redmine_xlsx_format_issue_exporter.json')
- .expectBadge({
- label: 'rating',
- message: Joi.string().regex(/^[0-9]+\.[0-9]+\/5\.0$/),
- })
-
-t.create('plugin stars')
- .get('/plugin/stars/redmine_xlsx_format_issue_exporter.json')
- .expectBadge({
- label: 'stars',
- message: isStarRating,
- })
-
-t.create('plugin not found')
- .get('/plugin/rating/plugin_not_found.json')
- .expectBadge({
- label: 'redmine',
- message: 'not found',
- })
diff --git a/services/repology/repology-repositories.service.js b/services/repology/repology-repositories.service.js
index 5a1ceb663355f..bd89fc5ef15a6 100644
--- a/services/repology/repology-repositories.service.js
+++ b/services/repology/repology-repositories.service.js
@@ -1,41 +1,35 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseSvgScrapingService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
const schema = Joi.object({
message: nonNegativeInteger,
}).required()
-module.exports = class RepologyRepositories extends BaseSvgScrapingService {
- static get category() {
- return 'platform-support'
- }
+export default class RepologyRepositories extends BaseSvgScrapingService {
+ static category = 'platform-support'
- static get route() {
- return {
- base: 'repology/repositories',
- pattern: ':projectName',
- }
+ static route = {
+ base: 'repology/repositories',
+ pattern: ':projectName',
}
- static get examples() {
- return [
- {
- title: 'Repology - Repositories',
- namedParams: { projectName: 'starship' },
- staticPreview: this.render({ repositoryCount: '18' }),
+ static openApi = {
+ '/repology/repositories/{projectName}': {
+ get: {
+ summary: 'Repology - Repositories',
+ parameters: pathParams({
+ name: 'projectName',
+ example: 'starship',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'repositories',
- color: 'blue',
- }
+ static defaultBadgeData = {
+ label: 'repositories',
+ color: 'blue',
}
static render({ repositoryCount }) {
diff --git a/services/repology/repology-repositories.tester.js b/services/repology/repology-repositories.tester.js
index 69a0a09ed379f..8250bec843d54 100644
--- a/services/repology/repology-repositories.tester.js
+++ b/services/repology/repology-repositories.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
+import { nonNegativeInteger } from '../validators.js'
+export const t = await createServiceTester()
-const t = (module.exports = require('../tester').createServiceTester())
-const { nonNegativeInteger } = require('../validators')
-
-t.create('Existing project')
- .get('/starship.json')
- .expectBadge({
- label: 'repositories',
- message: nonNegativeInteger,
- })
+t.create('Existing project').get('/starship.json').expectBadge({
+ label: 'repositories',
+ message: nonNegativeInteger,
+})
t.create('Non-existent project')
.get('/invalidprojectthatshouldnotexist.json')
diff --git a/services/reproducible-central/reproducible-central.service.js b/services/reproducible-central/reproducible-central.service.js
new file mode 100644
index 0000000000000..93f87920e8062
--- /dev/null
+++ b/services/reproducible-central/reproducible-central.service.js
@@ -0,0 +1,94 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object()
+ .pattern(Joi.string(), Joi.string().regex(/^\d+\/\d+$|^[X?]$/))
+ .required()
+
+const description = `
+[Reproducible Central](https://github.com/jvm-repo-rebuild/reproducible-central)
+provides [Reproducible Builds](https://reproducible-builds.org/) check status
+for projects published to [Maven Central](https://central.sonatype.com/).
+`
+
+export default class ReproducibleCentral extends BaseJsonService {
+ static category = 'dependencies'
+
+ static route = {
+ base: 'reproducible-central/artifact',
+ pattern: ':groupId/:artifactId/:version',
+ }
+
+ static openApi = {
+ '/reproducible-central/artifact/{groupId}/{artifactId}/{version}': {
+ get: {
+ summary: 'Reproducible Central Artifact',
+ description,
+ parameters: pathParams(
+ {
+ name: 'groupId',
+ example: 'org.apache.maven',
+ },
+ {
+ name: 'artifactId',
+ example: 'maven-core',
+ },
+ {
+ name: 'version',
+ example: '3.9.9',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'reproducible builds',
+ }
+
+ static render(rebuildResult) {
+ if (rebuildResult === undefined) {
+ return { color: 'red', message: 'version not available in Maven Central' }
+ } else if (rebuildResult === 'X') {
+ return { color: 'red', message: 'unable to rebuild' }
+ } else if (rebuildResult === '?') {
+ return { color: 'grey', message: 'version not evaluated' }
+ }
+
+ const [ok, count] = rebuildResult.split('/')
+ let color
+ if (ok === count) {
+ color = 'brightgreen'
+ } else if (ok > count - ok) {
+ color = 'yellow'
+ } else {
+ color = 'red'
+ }
+ return { color, message: rebuildResult }
+ }
+
+ async fetch({ groupId, artifactId }) {
+ return this._requestJson({
+ schema,
+ url: `https://jvm-repo-rebuild.github.io/reproducible-central/badge/artifact/${groupId.replace(/\./g, '/')}/${artifactId}.json`,
+ httpErrors: {
+ 404: 'groupId:artifactId unknown',
+ },
+ })
+ }
+
+ async handle({ groupId, artifactId, version }) {
+ if (version.endsWith('-SNAPSHOT')) {
+ return {
+ message: 'SNAPSHOT, not evaluated',
+ color: 'grey',
+ }
+ }
+
+ const versions = await this.fetch({
+ groupId,
+ artifactId,
+ })
+ return this.constructor.render(versions[version])
+ }
+}
diff --git a/services/reproducible-central/reproducible-central.tester.js b/services/reproducible-central/reproducible-central.tester.js
new file mode 100644
index 0000000000000..9a9857885b54c
--- /dev/null
+++ b/services/reproducible-central/reproducible-central.tester.js
@@ -0,0 +1,62 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('reproducible gav')
+ .get('/org.apache.maven/maven-core/3.9.9.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: '75/75',
+ color: 'brightgreen',
+ })
+
+t.create('mostly reproducible gav')
+ .get('/org.apache.maven/maven-core/3.8.5.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: '43/47',
+ color: 'yellow',
+ })
+
+t.create('mostly non-reproducible gav')
+ .get('/org.apache.maven/maven-core/3.6.3.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: '2/32',
+ color: 'red',
+ })
+
+t.create('non-rebuildable gav')
+ .get('/org.apache.maven/maven-core/4.0.0-alpha-2.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: 'unable to rebuild',
+ color: 'red',
+ })
+
+t.create('unknown v for known ga')
+ .get('/org.apache.maven/maven-core/3.9.9.1.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: 'version not available in Maven Central',
+ color: 'red',
+ })
+
+t.create('untested v for known ga')
+ .get('/org.apache.maven/maven-core/2.2.1.json')
+ .expectBadge({
+ label: 'reproducible builds',
+ message: 'version not evaluated',
+ color: 'grey',
+ })
+
+t.create('unknown ga').get('/org.apache.maven/any/3.9.9.json').expectBadge({
+ label: 'reproducible builds',
+ message: 'groupId:artifactId unknown',
+ color: 'red',
+})
+
+t.create('SNAPSHOT').get('/any/any/anything-SNAPSHOT.json').expectBadge({
+ label: 'reproducible builds',
+ message: 'SNAPSHOT, not evaluated',
+ color: 'grey',
+})
diff --git a/services/requires/requires.service.js b/services/requires/requires.service.js
deleted file mode 100644
index 64f13c239d5a5..0000000000000
--- a/services/requires/requires.service.js
+++ /dev/null
@@ -1,75 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const statusSchema = Joi.object({
- status: Joi.string().required(),
-}).required()
-
-module.exports = class RequiresIo extends BaseJsonService {
- static get category() {
- return 'dependencies'
- }
-
- static get route() {
- return {
- base: 'requires',
- pattern: ':service/:user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Requires.io',
- pattern: ':service/:user/:repo',
- namedParams: { service: 'github', user: 'celery', repo: 'celery' },
- staticPreview: this.render({ status: 'up-to-date' }),
- },
- {
- title: 'Requires.io (branch)',
- pattern: ':service/:user/:repo/:branch',
- namedParams: {
- service: 'github',
- user: 'celery',
- repo: 'celery',
- branch: 'master',
- },
- staticPreview: this.render({ status: 'up-to-date' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'requirements' }
- }
-
- static render({ status }) {
- let message = status
- let color = 'lightgrey'
- if (status === 'up-to-date') {
- message = 'up to date'
- color = 'brightgreen'
- } else if (status === 'outdated') {
- color = 'yellow'
- } else if (status === 'insecure') {
- color = 'red'
- }
- return { message, color }
- }
-
- async fetch({ service, user, repo, branch }) {
- const url = `https://requires.io/api/v1/status/${service}/${user}/${repo}`
- return this._requestJson({
- url,
- schema: statusSchema,
- options: { qs: { branch } },
- })
- }
-
- async handle({ service, user, repo, branch }) {
- const { status } = await this.fetch({ service, user, repo, branch })
- return this.constructor.render({ status })
- }
-}
diff --git a/services/requires/requires.tester.js b/services/requires/requires.tester.js
deleted file mode 100644
index 46751adc24696..0000000000000
--- a/services/requires/requires.tester.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isRequireStatus = Joi.string().regex(
- /^(up to date|outdated|insecure|unknown)$/
-)
-
-t.create('requirements (valid, without branch)')
- .get('/github/celery/celery.json')
- .expectBadge({
- label: 'requirements',
- message: isRequireStatus,
- })
-
-t.create('requirements (valid, with branch)')
- .get('/github/celery/celery/master.json')
- .expectBadge({
- label: 'requirements',
- message: isRequireStatus,
- })
-
-t.create('requirements (not found)')
- .get('/github/PyvesB/EmptyRepo.json')
- .expectBadge({ label: 'requirements', message: 'not found' })
diff --git a/services/resharper/resharper.service.js b/services/resharper/resharper.service.js
index 60585f7464721..8c6864e629709 100644
--- a/services/resharper/resharper.service.js
+++ b/services/resharper/resharper.service.js
@@ -1,16 +1,10 @@
-'use strict'
+import { createServiceFamily } from '../nuget/nuget-v2-service-family.js'
-const { createServiceFamily } = require('../nuget/nuget-v2-service-family')
-
-module.exports = createServiceFamily({
+export default createServiceFamily({
name: 'ResharperPlugin',
defaultLabel: 'resharper',
serviceBaseUrl: 'resharper',
apiBaseUrl: 'https://resharper-plugins.jetbrains.com/api/v2',
- odataFormat: 'xml',
title: 'JetBrains ReSharper plugins',
examplePackageName: 'StyleCop.StyleCop',
- exampleVersion: '2017.2.0',
- examplePrereleaseVersion: '2017.3.0-pre0001',
- exampleDownloadCount: 9e4,
})
diff --git a/services/resharper/resharper.tester.js b/services/resharper/resharper.tester.js
index 171d19c42185e..bee0fa6c5d948 100644
--- a/services/resharper/resharper.tester.js
+++ b/services/resharper/resharper.tester.js
@@ -1,21 +1,19 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const {
+import { ServiceTester } from '../tester.js'
+import {
isMetric,
isVPlusDottedVersionNClauses,
isVPlusDottedVersionNClausesWithOptionalSuffix,
-} = require('../test-validators')
+} from '../test-validators.js'
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'resharper',
title: 'ReSharper',
-}))
+})
// downloads
t.create('total downloads (valid)')
- .get('/dt/ReSharper.Nuke.json')
+ .get('/dt/StyleCop.StyleCop.json')
.expectBadge({
label: 'downloads',
message: isMetric,
@@ -27,12 +25,10 @@ t.create('total downloads (not found)')
// version
-t.create('version (valid)')
- .get('/v/ReSharper.Nuke.json')
- .expectBadge({
- label: 'resharper',
- message: isVPlusDottedVersionNClauses,
- })
+t.create('version (valid)').get('/v/StyleCop.StyleCop.json').expectBadge({
+ label: 'resharper',
+ message: isVPlusDottedVersionNClauses,
+})
t.create('version (not found)')
.get('/v/not-a-real-package.json')
@@ -41,7 +37,7 @@ t.create('version (not found)')
// version (pre)
t.create('version (pre) (valid)')
- .get('/v/ReSharper.Nuke.json?include_prereleases')
+ .get('/v/StyleCop.StyleCop.json?include_prereleases')
.expectBadge({
label: 'resharper',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
@@ -51,6 +47,9 @@ t.create('version (pre) (not found)')
.get('/v/not-a-real-package.json?include_prereleases')
.expectBadge({ label: 'resharper', message: 'not found' })
-t.create('version (legacy redirect: vpre)')
- .get('/vpre/ReSharper.Nuke.svg')
- .expectRedirect('/resharper/v/ReSharper.Nuke.svg?include_prereleases')
+t.create('version (legacy redirect: vpre - deprecated)')
+ .get('/vpre/StyleCop.StyleCop.json')
+ .expectBadge({
+ label: 'resharper',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
diff --git a/services/response-fixtures.js b/services/response-fixtures.js
index 18d7439e53af1..90557126f56ad 100644
--- a/services/response-fixtures.js
+++ b/services/response-fixtures.js
@@ -1,12 +1,7 @@
-'use strict'
+export const invalidJSONString = '{{{{{invalid json}}'
-const invalidJSONString = '{{{{{invalid json}}'
-
-module.exports = {
- invalidJSON: () => [
- 200,
- invalidJSONString,
- { 'Content-Type': 'application/json' },
- ],
+export const invalidJSON = () => [
+ 200,
invalidJSONString,
-}
+ { 'Content-Type': 'application/json' },
+]
diff --git a/services/reuse/reuse-compliance-helper.js b/services/reuse/reuse-compliance-helper.js
new file mode 100644
index 0000000000000..7503696bb373c
--- /dev/null
+++ b/services/reuse/reuse-compliance-helper.js
@@ -0,0 +1,14 @@
+import Joi from 'joi'
+
+const COLOR_MAP = {
+ checking: 'brightgreen',
+ compliant: 'green',
+ 'non-compliant': 'red',
+ unregistered: 'red',
+}
+
+const isReuseCompliance = Joi.string()
+ .valid('compliant', 'non-compliant', 'checking', 'unregistered')
+ .required()
+
+export { isReuseCompliance, COLOR_MAP }
diff --git a/services/reuse/reuse-compliance.service.js b/services/reuse/reuse-compliance.service.js
new file mode 100644
index 0000000000000..e5a8715da90e8
--- /dev/null
+++ b/services/reuse/reuse-compliance.service.js
@@ -0,0 +1,53 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { isReuseCompliance, COLOR_MAP } from './reuse-compliance-helper.js'
+
+const responseSchema = Joi.object({
+ status: isReuseCompliance,
+}).required()
+
+export default class Reuse extends BaseJsonService {
+ static category = 'license'
+
+ static route = {
+ base: 'reuse/compliance',
+ pattern: ':remote+',
+ }
+
+ static openApi = {
+ '/reuse/compliance/{remote}': {
+ get: {
+ summary: 'REUSE Compliance',
+ parameters: pathParams({
+ name: 'remote',
+ example: 'github.com/fsfe/reuse-tool',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'reuse',
+ }
+
+ static render({ status }) {
+ return {
+ label: 'reuse',
+ message: status,
+ color: COLOR_MAP[status],
+ }
+ }
+
+ async fetch({ remote }) {
+ return await this._requestJson({
+ schema: responseSchema,
+ url: `https://api.reuse.software/status/${remote}`,
+ httpErrors: { 404: 'unregistered' },
+ })
+ }
+
+ async handle({ remote }) {
+ const { status } = await this.fetch({ remote })
+ return this.constructor.render({ status })
+ }
+}
diff --git a/services/reuse/reuse-compliance.tester.js b/services/reuse/reuse-compliance.tester.js
new file mode 100644
index 0000000000000..5a6c02f5e911f
--- /dev/null
+++ b/services/reuse/reuse-compliance.tester.js
@@ -0,0 +1,58 @@
+import { createServiceTester } from '../tester.js'
+import { isReuseCompliance, COLOR_MAP } from './reuse-compliance-helper.js'
+export const t = await createServiceTester()
+
+t.create('valid repo -- live')
+ .get('/github.com/fsfe/reuse-tool.json')
+ .expectBadge({
+ label: 'reuse',
+ message: isReuseCompliance,
+ color: COLOR_MAP[isReuseCompliance],
+ })
+
+t.create('valid repo -- compliant')
+ .get('/github.com/username/repo.json')
+ .intercept(nock =>
+ nock('https://api.reuse.software/status')
+ .get('/github.com/username/repo')
+ .reply(200, { status: 'compliant' }),
+ )
+ .expectBadge({
+ label: 'reuse',
+ message: 'compliant',
+ color: COLOR_MAP.compliant,
+ })
+
+t.create('valid repo -- non-compliant')
+ .get('/github.com/username/repo.json')
+ .intercept(nock =>
+ nock('https://api.reuse.software/status')
+ .get('/github.com/username/repo')
+ .reply(200, { status: 'non-compliant' }),
+ )
+ .expectBadge({
+ label: 'reuse',
+ message: 'non-compliant',
+ color: COLOR_MAP['non-compliant'],
+ })
+
+t.create('valid repo -- checking')
+ .get('/github.com/username/repo.json')
+ .intercept(nock =>
+ nock('https://api.reuse.software/status')
+ .get('/github.com/username/repo')
+ .reply(200, { status: 'checking' }),
+ )
+ .expectBadge({
+ label: 'reuse',
+ message: 'checking',
+ color: COLOR_MAP.checking,
+ })
+
+t.create('valid repo -- unregistered')
+ .get('/github.com/badges/shields.json')
+ .expectBadge({
+ label: 'reuse',
+ message: 'unregistered',
+ color: COLOR_MAP.unregistered,
+ })
diff --git a/services/revolt/revolt.service.js b/services/revolt/revolt.service.js
new file mode 100644
index 0000000000000..328a5f28d1269
--- /dev/null
+++ b/services/revolt/revolt.service.js
@@ -0,0 +1,80 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger, optionalUrl } from '../validators.js'
+
+const schema = Joi.object({
+ member_count: nonNegativeInteger,
+}).required()
+
+const description = `
+The Revolt badge requires an INVITE CODE to access the Revolt API,
+which can be located at the end of the invitation url.
+
+For example, both
+https://app.revolt.chat/invite/01F7ZSBSFHQ8TA81725KQCSDDP and
+https://rvlt.gg/01F7ZSBSFHQ8TA81725KQCSDDP contains an invite code
+of 01F7ZSBSFHQ8TA81725KQCSDDP.
+`
+
+const queryParamSchema = Joi.object({
+ revolt_api_url: optionalUrl,
+}).required()
+
+export default class RevoltServerInvite extends BaseJsonService {
+ static category = 'chat'
+
+ static route = {
+ base: 'revolt/invite',
+ pattern: ':inviteId',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/revolt/invite/{inviteId}': {
+ get: {
+ summary: 'Revolt',
+ description,
+ parameters: [
+ pathParam({
+ name: 'inviteId',
+ example: '01F7ZSBSFHQ8TA81725KQCSDDP',
+ }),
+ queryParam({
+ name: 'revolt_api_url',
+ example: 'https://api.revolt.chat',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'chat' }
+
+ static render({ memberCount }) {
+ return {
+ message: `${metric(memberCount)} members`,
+ color: 'brightgreen',
+ }
+ }
+
+ async fetch({ inviteId, baseUrl }) {
+ return this._requestJson({
+ schema,
+ url: `${baseUrl}/invites/${inviteId}`,
+ })
+ }
+
+ async handle(
+ { inviteId },
+ { revolt_api_url: baseUrl = 'https://api.revolt.chat' },
+ ) {
+ const { member_count: memberCount } = await this.fetch({
+ inviteId,
+ baseUrl,
+ })
+ return this.constructor.render({
+ memberCount,
+ })
+ }
+}
diff --git a/services/revolt/revolt.tester.js b/services/revolt/revolt.tester.js
new file mode 100644
index 0000000000000..3b2eb963eb222
--- /dev/null
+++ b/services/revolt/revolt.tester.js
@@ -0,0 +1,26 @@
+import { createServiceTester } from '../tester.js'
+import { isMetricWithPattern } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('get status of #revolt')
+ .get('/01F7ZSBSFHQ8TA81725KQCSDDP.json')
+ .expectBadge({
+ label: 'chat',
+ message: isMetricWithPattern(/ members/),
+ color: 'brightgreen',
+ })
+
+t.create('custom api url')
+ .get(
+ '/01F7ZSBSFHQ8TA81725KQCSDDP.json?revolt_api_url=https://api.revolt.chat',
+ )
+ .expectBadge({
+ label: 'chat',
+ message: isMetricWithPattern(/ members/),
+ color: 'brightgreen',
+ })
+
+t.create('invalid invite code')
+ .get('/12345.json')
+ .expectBadge({ label: 'chat', message: 'not found' })
diff --git a/services/ros/ros-version.service.js b/services/ros/ros-version.service.js
new file mode 100644
index 0000000000000..1cd3739a7ded6
--- /dev/null
+++ b/services/ros/ros-version.service.js
@@ -0,0 +1,171 @@
+import gql from 'graphql-tag'
+import Joi from 'joi'
+import * as yaml from 'js-yaml'
+import { renderVersionBadge } from '../version.js'
+import { GithubAuthV4Service } from '../github/github-auth-service.js'
+import { NotFound, InvalidResponse, pathParams } from '../index.js'
+
+const tagsSchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ refs: Joi.object({
+ edges: Joi.array()
+ .items({
+ node: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ })
+ .required(),
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const contentSchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ object: Joi.object({
+ text: Joi.string().required(),
+ }).allow(null),
+ }).required(),
+ }).required(),
+}).required()
+
+const distroSchema = Joi.object({
+ repositories: Joi.object().required(),
+})
+const repoSchema = Joi.object({
+ release: Joi.object({
+ version: Joi.string().required(),
+ }).required(),
+})
+
+const description = `
+To use this badge, specify the ROS distribution
+(e.g. noetic or humble) and the package repository name
+(in the case of single-package repos, this may be the same as the package name).
+This badge determines which versions are part of an official ROS distribution by
+fetching from the rosdistro YAML files,
+at the tag corresponding to the latest release.
+`
+
+export default class RosVersion extends GithubAuthV4Service {
+ static category = 'version'
+
+ static route = { base: 'ros/v', pattern: ':distro/:repoName' }
+
+ static openApi = {
+ '/ros/v/{distro}/{repoName}': {
+ get: {
+ summary: 'ROS Package Index',
+ description,
+ parameters: pathParams(
+ {
+ name: 'distro',
+ example: 'humble',
+ },
+ {
+ name: 'repoName',
+ example: 'vision_msgs',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'ros' }
+
+ async handle({ distro, repoName }) {
+ const tagsJson = await this._requestGraphql({
+ query: gql`
+ query ($refPrefix: String!) {
+ repository(owner: "ros", name: "rosdistro") {
+ refs(
+ refPrefix: $refPrefix
+ first: 30
+ orderBy: { field: TAG_COMMIT_DATE, direction: DESC }
+ ) {
+ edges {
+ node {
+ name
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: { refPrefix: `refs/tags/${distro}/` },
+ schema: tagsSchema,
+ })
+
+ // Filter for tags that look like dates: humble/2022-06-10
+ const tags = tagsJson.data.repository.refs.edges
+ .map(edge => edge.node.name)
+ .filter(tag => /^\d+-\d+-\d+$/.test(tag))
+ .sort()
+ .reverse()
+
+ const ref = tags[0] ? `refs/tags/${distro}/${tags[0]}` : 'refs/heads/master'
+ const prettyRef = tags[0] ? `${distro}/${tags[0]}` : 'master'
+
+ const contentJson = await this._requestGraphql({
+ query: gql`
+ query ($expression: String!) {
+ repository(owner: "ros", name: "rosdistro") {
+ object(expression: $expression) {
+ ... on Blob {
+ text
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ expression: `${ref}:${distro}/distribution.yaml`,
+ },
+ schema: contentSchema,
+ })
+
+ if (!contentJson.data.repository.object) {
+ throw new NotFound({
+ prettyMessage: `distribution.yaml not found: ${distro}@${prettyRef}`,
+ })
+ }
+ const version = this.constructor._parseReleaseVersionFromDistro(
+ contentJson.data.repository.object.text,
+ repoName,
+ )
+
+ return { ...renderVersionBadge({ version }), label: `ros | ${distro}` }
+ }
+
+ static _parseReleaseVersionFromDistro(distroYaml, repoName) {
+ let distro
+ try {
+ distro = yaml.load(distroYaml)
+ } catch (err) {
+ throw new InvalidResponse({
+ prettyMessage: 'unparseable distribution.yml',
+ underlyingError: err,
+ })
+ }
+
+ const validatedDistro = this._validate(distro, distroSchema, {
+ prettyErrorMessage: 'invalid distribution.yml',
+ })
+ if (!validatedDistro.repositories[repoName]) {
+ throw new NotFound({ prettyMessage: `repo not found: ${repoName}` })
+ }
+
+ const repoInfo = this._validate(
+ validatedDistro.repositories[repoName],
+ repoSchema,
+ {
+ prettyErrorMessage: `invalid section for ${repoName} in distribution.yml`,
+ },
+ )
+
+ // Strip off "release inc" suffix
+ return repoInfo.release.version.replace(/-\d+$/, '')
+ }
+}
diff --git a/services/ros/ros-version.service.spec.js b/services/ros/ros-version.service.spec.js
new file mode 100644
index 0000000000000..c6c2d34956ec5
--- /dev/null
+++ b/services/ros/ros-version.service.spec.js
@@ -0,0 +1,44 @@
+import { expect } from 'chai'
+import RosVersion from './ros-version.service.js'
+
+describe('parseReleaseVersionFromDistro', function () {
+ it('returns correct version', function () {
+ expect(
+ RosVersion._parseReleaseVersionFromDistro(
+ `
+%YAML 1.1
+# ROS distribution file
+# see REP 143: http://ros.org/reps/rep-0143.html
+---
+release_platforms:
+ debian:
+ - bullseye
+ rhel:
+ - '8'
+ ubuntu:
+ - jammy
+repositories:
+ vision_msgs:
+ doc:
+ type: git
+ url: https://github.com/ros-perception/vision_msgs.git
+ version: ros2
+ release:
+ tags:
+ release: release/humble/{package}/{version}
+ url: https://github.com/ros2-gbp/vision_msgs-release.git
+ version: 4.0.0-2
+ source:
+ test_pull_requests: true
+ type: git
+ url: https://github.com/ros-perception/vision_msgs.git
+ version: ros2
+ status: developed
+type: distribution
+version: 2
+ `,
+ 'vision_msgs',
+ ),
+ ).to.equal('4.0.0')
+ })
+})
diff --git a/services/ros/ros-version.tester.js b/services/ros/ros-version.tester.js
new file mode 100644
index 0000000000000..4e7e3c7d6d6f3
--- /dev/null
+++ b/services/ros/ros-version.tester.js
@@ -0,0 +1,28 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('gets the version of vision_msgs in active distro')
+ .get('/humble/vision_msgs.json')
+ .expectBadge({ label: 'ros | humble', message: isSemver })
+
+t.create('gets the version of vision_msgs in EOL distro')
+ .get('/lunar/vision_msgs.json')
+ .expectBadge({ label: 'ros | lunar', message: isSemver })
+
+t.create('returns not found for invalid repo')
+ .get('/humble/this repo does not exist - ros test.json')
+ .expectBadge({
+ label: 'ros',
+ color: 'red',
+ message: 'repo not found: this repo does not exist - ros test',
+ })
+
+t.create('returns error for invalid distro')
+ .get('/xxxxxx/vision_msgs.json')
+ .expectBadge({
+ label: 'ros',
+ color: 'red',
+ message: 'distribution.yaml not found: xxxxxx@master',
+ })
diff --git a/services/route-builder.js b/services/route-builder.js
index 99679f316176a..d9f341b410158 100644
--- a/services/route-builder.js
+++ b/services/route-builder.js
@@ -1,16 +1,26 @@
-'use strict'
+/**
+ * Common functions and utilities for tasks related to route building.
+ *
+ * @module
+ */
-const toArray = require('../core/base-service/to-array')
+import toArray from '../core/base-service/to-array.js'
-/*
+/**
* Factory class for building a BaseService `route` object. This class is useful
* in complex collections of service classes, when the URL is built
* conditionally.
*
* Patterns based on path-to-regex may obviate the need for this, though they
* haven't done so yet.
+ *
+ * @class
*/
-module.exports = class RouteBuilder {
+export default class RouteBuilder {
+ /**
+ * @param {object} attrs - Refer to individual attributes
+ * @param {string} attrs.base - Base URL, defaults to ''
+ */
constructor({ base = '' } = {}) {
this.base = base
@@ -18,10 +28,22 @@ module.exports = class RouteBuilder {
this.capture = []
}
+ /**
+ * Get the format components separated by '/'.
+ *
+ * @returns {string} Format components, for example: "format1/format2/format3".
+ */
get format() {
return this._formatComponents.join('/')
}
+ /**
+ * Saves the format and capture values in the `RouteBuilder` instance.
+ *
+ * @param {string} format - Pattern based on path-to-regex, for example: `(?:(.+)\\.)?${serviceBaseUrl}`
+ * @param {string} capture - Value to capture
+ * @returns {object} RouteBuilder instance for chaining
+ */
push(format, capture) {
this._formatComponents = this._formatComponents.concat(toArray(format))
this.capture = this.capture.concat(toArray(capture))
@@ -29,6 +51,11 @@ module.exports = class RouteBuilder {
return this
}
+ /**
+ * Returns a new object based on `RouteBuilder` instance containing its base, format and capture properties.
+ *
+ * @returns {{base: string, format: string, capture: string[]}} Object containing base, format and capture properties of the `RouteBuilder` instance
+ */
toObject() {
const { base, format, capture } = this
return { base, format, capture }
diff --git a/services/scoop/scoop-base.js b/services/scoop/scoop-base.js
new file mode 100644
index 0000000000000..6af71f83cf3ac
--- /dev/null
+++ b/services/scoop/scoop-base.js
@@ -0,0 +1,81 @@
+import Joi from 'joi'
+import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
+import { fetchJsonFromRepo } from '../github/github-common-fetch.js'
+import { NotFound } from '../index.js'
+
+const gitHubRepoRegExp =
+ /https:\/\/github.com\/(?.*?)\/(?.*?)(\/|$)/
+
+const bucketsSchema = Joi.object()
+ .pattern(/.+/, Joi.string().pattern(gitHubRepoRegExp).required())
+ .required()
+
+export const queryParamSchema = Joi.object({
+ bucket: Joi.string(),
+})
+
+export class ScoopBase extends ConditionalGithubAuthV3Service {
+ // The buckets file (https://github.com/lukesampson/scoop/blob/master/buckets.json) changes very rarely.
+ // Cache it for the lifetime of the current Node.js process.
+ buckets = null
+
+ async fetch({ app, schema }, queryParams) {
+ if (!this.buckets) {
+ this.buckets = await fetchJsonFromRepo(this, {
+ schema: bucketsSchema,
+ user: 'ScoopInstaller',
+ repo: 'Scoop',
+ branch: 'master',
+ filename: 'buckets.json',
+ })
+ }
+ const bucket = queryParams.bucket || 'main'
+ let bucketUrl = this.buckets[bucket]
+ if (!bucketUrl) {
+ // Parsing URL here will throw an error if the url is invalid
+ try {
+ const url = new URL(decodeURIComponent(bucket))
+
+ // Throw errors to go to jump to catch statement
+ // The error messages here are purely for code readability, and will never reach the user.
+ if (url.hostname !== 'github.com') {
+ throw new Error('Not a GitHub URL')
+ }
+ const path = url.pathname.split('/').filter(value => value !== '')
+
+ if (path.length !== 2) {
+ throw new Error('Not a valid GitHub Repo')
+ }
+
+ const [user, repo] = path
+
+ // Reconstructing the url here ensures that the url will match the regex
+ bucketUrl = `https://github.com/${user}/${repo}`
+ } catch (e) {
+ throw new NotFound({ prettyMessage: `bucket "${bucket}" not found` })
+ }
+ }
+ const {
+ groups: { user, repo },
+ } = gitHubRepoRegExp.exec(bucketUrl)
+ try {
+ return await fetchJsonFromRepo(this, {
+ schema,
+ user,
+ repo,
+ branch: 'master',
+ filename: `bucket/${app}.json`,
+ })
+ } catch (error) {
+ if (error instanceof NotFound) {
+ throw new NotFound({
+ prettyMessage: `${app} not found in bucket "${bucket}"`,
+ })
+ }
+ throw error
+ }
+ }
+}
+
+export const description =
+ '[Scoop](https://scoop.sh/) is a command-line installer for Windows'
diff --git a/services/scoop/scoop-license.service.js b/services/scoop/scoop-license.service.js
new file mode 100644
index 0000000000000..be7be0b473106
--- /dev/null
+++ b/services/scoop/scoop-license.service.js
@@ -0,0 +1,63 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { renderLicenseBadge } from '../licenses.js'
+import toArray from '../../core/base-service/to-array.js'
+import { queryParamSchema, description, ScoopBase } from './scoop-base.js'
+
+const scoopLicenseSchema = Joi.object({
+ license: Joi.alternatives()
+ .try(
+ Joi.string().required(),
+ Joi.object({
+ identifier: Joi.string().required(),
+ }),
+ )
+ .required(),
+}).required()
+
+export default class ScoopLicense extends ScoopBase {
+ static category = 'license'
+
+ static route = {
+ base: 'scoop/l',
+ pattern: ':app',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/scoop/l/{app}': {
+ get: {
+ summary: 'Scoop License',
+ description,
+ parameters: [
+ pathParam({ name: 'app', example: 'ngrok' }),
+ queryParam({
+ name: 'bucket',
+ description:
+ "App's containing bucket. Can either be a name (e.g `extras`) or a URL to a GitHub Repo (e.g `https://github.com/jewlexx/personal-scoop`)",
+ example: 'extras',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'license' }
+
+ static render({ licenses }) {
+ return renderLicenseBadge({ licenses })
+ }
+
+ async handle({ app }, queryParams) {
+ const { license } = await this.fetch(
+ { app, schema: scoopLicenseSchema },
+ queryParams,
+ )
+
+ const licenses = toArray(license).map(license =>
+ typeof license === 'string' ? license : license.identifier,
+ )
+
+ return this.constructor.render({ licenses })
+ }
+}
diff --git a/services/scoop/scoop-license.tester.js b/services/scoop/scoop-license.tester.js
new file mode 100644
index 0000000000000..f69ebad9dd349
--- /dev/null
+++ b/services/scoop/scoop-license.tester.js
@@ -0,0 +1,94 @@
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('License (valid) - with nested response')
+ .get('/ngrok.json')
+ .expectBadge({
+ label: 'license',
+ message: 'Freeware',
+ })
+
+t.create('License (valid) - with string response')
+ .get('/nvs.json')
+ .expectBadge({
+ label: 'license',
+ message: 'MIT',
+ })
+
+t.create('License (invalid)').get('/not-a-real-app.json').expectBadge({
+ label: 'license',
+ message: 'not-a-real-app not found in bucket "main"',
+})
+
+t.create('License (valid custom bucket)')
+ .get('/atom.json?bucket=extras')
+ .expectBadge({
+ label: 'license',
+ message: 'MIT',
+ })
+
+t.create('license (not found in custom bucket)')
+ .get('/not-a-real-app.json?bucket=extras')
+ .expectBadge({
+ label: 'license',
+ message: 'not-a-real-app not found in bucket "extras"',
+ })
+
+t.create('license (wrong bucket)')
+ .get('/not-a-real-app.json?bucket=not-a-real-bucket')
+ .expectBadge({
+ label: 'license',
+ message: 'bucket "not-a-real-bucket" not found',
+ })
+
+// version (bucket url)
+const validBucketUrl = encodeURIComponent(
+ 'https://github.com/jewlexx/personal-scoop',
+)
+
+t.create('license (valid bucket url)')
+ .get(`/sfsu.json?bucket=${validBucketUrl}`)
+ .expectBadge({
+ label: 'license',
+ message: 'Apache-2.0',
+ })
+
+const validBucketUrlTrailingSlash = encodeURIComponent(
+ 'https://github.com/jewlexx/personal-scoop/',
+)
+
+t.create('license (valid bucket url)')
+ .get(`/sfsu.json?bucket=${validBucketUrlTrailingSlash}`)
+ .expectBadge({
+ label: 'license',
+ message: 'Apache-2.0',
+ })
+
+t.create('license (not found in custom bucket)')
+ .get(`/not-a-real-app.json?bucket=${validBucketUrl}`)
+ .expectBadge({
+ label: 'license',
+ message: `not-a-real-app not found in bucket "${decodeURIComponent(validBucketUrl)}"`,
+ })
+
+const nonGithubUrl = encodeURIComponent('https://example.com/')
+
+t.create('license (non-github url)')
+ .get(`/not-a-real-app.json?bucket=${nonGithubUrl}`)
+ .expectBadge({
+ label: 'license',
+ message: `bucket "${decodeURIComponent(nonGithubUrl)}" not found`,
+ })
+
+const nonBucketRepo = encodeURIComponent('https://github.com/jewlexx/sfsu')
+
+t.create('version (non-bucket repo)')
+ .get(`/sfsu.json?bucket=${nonBucketRepo}`)
+ .expectBadge({
+ label: 'license',
+ // !!! Important note here
+ // It is hard to tell if a repo is actually a scoop bucket, without getting the contents
+ // As such, a helpful error message here, which would require testing if the url is a valid scoop bucket, is difficult.
+ message: `sfsu not found in bucket "${decodeURIComponent(nonBucketRepo)}"`,
+ })
diff --git a/services/scoop/scoop-version.service.js b/services/scoop/scoop-version.service.js
new file mode 100644
index 0000000000000..1676959b5752a
--- /dev/null
+++ b/services/scoop/scoop-version.service.js
@@ -0,0 +1,51 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { queryParamSchema, description, ScoopBase } from './scoop-base.js'
+
+const scoopSchema = Joi.object({
+ version: Joi.string().required(),
+}).required()
+
+export default class ScoopVersion extends ScoopBase {
+ static category = 'version'
+
+ static route = {
+ base: 'scoop/v',
+ pattern: ':app',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/scoop/v/{app}': {
+ get: {
+ summary: 'Scoop Version',
+ description,
+ parameters: [
+ pathParam({ name: 'app', example: 'ngrok' }),
+ queryParam({
+ name: 'bucket',
+ description:
+ "App's containing bucket. Can either be a name (e.g `extras`) or a URL to a GitHub Repo (e.g `https://github.com/jewlexx/personal-scoop`)",
+ example: 'extras',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'scoop' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async handle({ app }, queryParams) {
+ const { version } = await this.fetch(
+ { app, schema: scoopSchema },
+ queryParams,
+ )
+
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/scoop/scoop-version.tester.js b/services/scoop/scoop-version.tester.js
new file mode 100644
index 0000000000000..8c25e03ccf0f7
--- /dev/null
+++ b/services/scoop/scoop-version.tester.js
@@ -0,0 +1,93 @@
+import { ServiceTester } from '../tester.js'
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+
+export const t = new ServiceTester({
+ id: 'scoop',
+ title: 'Scoop',
+})
+
+// version
+
+t.create('version (valid)').get('/v/apache.json').expectBadge({
+ label: 'scoop',
+ message: isVPlusDottedVersionNClauses,
+})
+
+t.create('version (not found)').get('/v/not-a-real-app.json').expectBadge({
+ label: 'scoop',
+ message: 'not-a-real-app not found in bucket "main"',
+})
+
+// version (custom bucket)
+
+t.create('version (valid custom bucket)')
+ .get('/v/dnspy.json?bucket=extras')
+ .expectBadge({
+ label: 'scoop',
+ message: isVPlusDottedVersionNClauses,
+ })
+
+t.create('version (not found in custom bucket)')
+ .get('/v/not-a-real-app.json?bucket=extras')
+ .expectBadge({
+ label: 'scoop',
+ message: 'not-a-real-app not found in bucket "extras"',
+ })
+
+t.create('version (wrong bucket)')
+ .get('/v/not-a-real-app.json?bucket=not-a-real-bucket')
+ .expectBadge({
+ label: 'scoop',
+ message: 'bucket "not-a-real-bucket" not found',
+ })
+
+// version (bucket url)
+const validBucketUrl = encodeURIComponent(
+ 'https://github.com/jewlexx/personal-scoop',
+)
+
+t.create('version (valid bucket url)')
+ .get(`/v/sfsu.json?bucket=${validBucketUrl}`)
+ .expectBadge({
+ label: 'scoop',
+ message: isVPlusDottedVersionNClauses,
+ })
+
+const validBucketUrlTrailingSlash = encodeURIComponent(
+ 'https://github.com/jewlexx/personal-scoop/',
+)
+
+t.create('version (valid bucket url)')
+ .get(`/v/sfsu.json?bucket=${validBucketUrlTrailingSlash}`)
+ .expectBadge({
+ label: 'scoop',
+ message: isVPlusDottedVersionNClauses,
+ })
+
+t.create('version (not found in custom bucket)')
+ .get(`/v/not-a-real-app.json?bucket=${validBucketUrl}`)
+ .expectBadge({
+ label: 'scoop',
+ message: `not-a-real-app not found in bucket "${decodeURIComponent(validBucketUrl)}"`,
+ })
+
+const nonGithubUrl = encodeURIComponent('https://example.com/')
+
+t.create('version (non-github url)')
+ .get(`/v/not-a-real-app.json?bucket=${nonGithubUrl}`)
+ .expectBadge({
+ label: 'scoop',
+ message: `bucket "${decodeURIComponent(nonGithubUrl)}" not found`,
+ })
+
+const nonBucketRepo = encodeURIComponent('https://github.com/jewlexx/sfsu')
+
+t.create('version (non-bucket repo)')
+ .get(`/v/sfsu.json?bucket=${nonBucketRepo}`)
+ .expectBadge({
+ label: 'scoop',
+ // !!! Important note here
+ // It is hard to tell if a repo is actually a scoop bucket, without getting the contents
+ // As such, a helpful error message here, which would require testing if the url is a valid scoop bucket, is difficult.
+ message: `sfsu not found in bucket "${decodeURIComponent(nonBucketRepo)}"`,
+ })
diff --git a/services/scrutinizer/scrutinizer-base.js b/services/scrutinizer/scrutinizer-base.js
index b2a7c59cab849..90f4fe9062f35 100644
--- a/services/scrutinizer/scrutinizer-base.js
+++ b/services/scrutinizer/scrutinizer-base.js
@@ -1,14 +1,12 @@
-'use strict'
+import { BaseJsonService, NotFound, InvalidResponse } from '../index.js'
-const { BaseJsonService, NotFound, InvalidResponse } = require('..')
-
-module.exports = class ScrutinizerBase extends BaseJsonService {
+export default class ScrutinizerBase extends BaseJsonService {
// https://scrutinizer-ci.com/docs/api/#repository-details
async fetch({ schema, vcs, slug }) {
return this._requestJson({
schema,
url: `https://scrutinizer-ci.com/api/repositories/${vcs}/${slug}`,
- errorMessages: {
+ httpErrors: {
401: 'not authorized to access project',
404: 'project not found',
},
diff --git a/services/scrutinizer/scrutinizer-build.service.js b/services/scrutinizer/scrutinizer-build.service.js
index 1166fd11248bb..975838644e73e 100644
--- a/services/scrutinizer/scrutinizer-build.service.js
+++ b/services/scrutinizer/scrutinizer-build.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const ScrutinizerBase = require('./scrutinizer-base')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { pathParams } from '../index.js'
+import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
default_branch: Joi.string().required(),
@@ -13,20 +12,16 @@ const schema = Joi.object({
build_status: Joi.object({
status: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
}).required(),
- })
+ }),
)
.required(),
}).required()
class ScrutinizerBuildBase extends ScrutinizerBase {
- static get category() {
- return 'build'
- }
+ static category = 'build'
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
+ static defaultBadgeData = {
+ label: 'build',
}
async makeBadge({ vcs, slug, branch }) {
@@ -39,27 +34,43 @@ class ScrutinizerBuildBase extends ScrutinizerBase {
}
class ScrutinizerBuild extends ScrutinizerBuildBase {
- static get route() {
- return {
- base: 'scrutinizer/build',
- pattern: ':vcs(g|b)/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/build',
+ pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'Scrutinizer build (GitHub/Bitbucket)',
- pattern: ':vcs(g|b)/:user/:repo/:branch?',
- namedParams: {
- vcs: 'g',
- user: 'filp',
- repo: 'whoops',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+ static openApi = {
+ '/scrutinizer/build/{vcs}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer build (GitHub/Bitbucket)',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ ),
},
- ]
+ },
+ '/scrutinizer/build/{vcs}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer build (GitHub/Bitbucket) with branch',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ { name: 'branch', example: 'master' },
+ ),
+ },
+ },
}
async handle({ vcs, user, repo, branch }) {
@@ -72,31 +83,37 @@ class ScrutinizerBuild extends ScrutinizerBuildBase {
}
class ScrutinizerGitLabBuild extends ScrutinizerBuildBase {
- static get route() {
- return {
- base: 'scrutinizer/build/gl',
- pattern: ':instance/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/build/gl',
+ pattern: ':instance/:user/:repo/:branch*',
}
- static get examples() {
- // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
- // The example used is valid, but the project will not be accessible if Shields users try to use it.
- // https://gitlab.propertywindow.nl/propertywindow/client
- // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
- return [
- {
- title: 'Scrutinizer build (GitLab)',
- pattern: ':instance/:user/:repo/:branch?',
- namedParams: {
- instance: 'propertywindow',
- user: 'propertywindow',
- repo: 'client',
- branch: 'master',
- },
- staticPreview: renderBuildStatusBadge({ status: 'passing' }),
+ // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
+ // The example used is valid, but the project will not be accessible if Shields users try to use it.
+ // https://gitlab.propertywindow.nl/propertywindow/client
+ // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
+ static openApi = {
+ '/scrutinizer/build/gl/{instance}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer build (GitLab)',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ ),
+ },
+ },
+ '/scrutinizer/build/gl/{instance}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer build (GitLab) with branch',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
async handle({ instance, user, repo, branch }) {
@@ -109,11 +126,9 @@ class ScrutinizerGitLabBuild extends ScrutinizerBuildBase {
}
class ScrutinizerPlainGitBuild extends ScrutinizerBuildBase {
- static get route() {
- return {
- base: 'scrutinizer/build/gp',
- pattern: ':slug/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/build/gp',
+ pattern: ':slug/:branch*',
}
async handle({ slug, branch }) {
@@ -121,7 +136,7 @@ class ScrutinizerPlainGitBuild extends ScrutinizerBuildBase {
}
}
-module.exports = [
+export default [
ScrutinizerBuild,
ScrutinizerGitLabBuild,
ScrutinizerPlainGitBuild,
diff --git a/services/scrutinizer/scrutinizer-build.tester.js b/services/scrutinizer/scrutinizer-build.tester.js
index 5547c9f4778da..00223515061fe 100644
--- a/services/scrutinizer/scrutinizer-build.tester.js
+++ b/services/scrutinizer/scrutinizer-build.tester.js
@@ -1,13 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import Joi from 'joi'
+import { isBuildStatus } from '../build-status.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'ScrutinizerBuild',
title: 'ScrutinizerBuild',
pathPrefix: '/scrutinizer/build',
-}))
+})
t.create('build (GitHub)')
.get('/g/filp/whoops.json')
@@ -44,7 +42,7 @@ t.create('build - unknown status')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -59,12 +57,10 @@ t.create('build private project')
message: 'not authorized to access project',
})
-t.create('build nonexistent project')
- .get('/gp/foo.json')
- .expectBadge({
- label: 'build',
- message: 'project not found',
- })
+t.create('build nonexistent project').get('/gp/foo.json').expectBadge({
+ label: 'build',
+ message: 'project not found',
+})
t.create('build nonexistent branch')
.get('/g/phpmyadmin/phpmyadmin/super-fake/not-real-branch.json')
diff --git a/services/scrutinizer/scrutinizer-coverage.service.js b/services/scrutinizer/scrutinizer-coverage.service.js
index 4ded0fc03fb21..ce3fd05b53a6c 100644
--- a/services/scrutinizer/scrutinizer-coverage.service.js
+++ b/services/scrutinizer/scrutinizer-coverage.service.js
@@ -1,9 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale } = require('../color-formatters')
-const { NotFound } = require('..')
-const ScrutinizerBase = require('./scrutinizer-base')
+import Joi from 'joi'
+import { colorScale } from '../color-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
default_branch: Joi.string().required(),
@@ -20,7 +18,7 @@ const schema = Joi.object({
}).required(),
}).required(),
}),
- })
+ }),
)
.required(),
}).required()
@@ -32,14 +30,10 @@ const schema = Joi.object({
const scale = colorScale([40, 61], ['red', 'yellow', 'brightgreen'])
class ScrutinizerCoverageBase extends ScrutinizerBase {
- static get category() {
- return 'coverage'
- }
+ static category = 'coverage'
- static get defaultBadgeData() {
- return {
- label: 'coverage',
- }
+ static defaultBadgeData = {
+ label: 'coverage',
}
static render({ coverage }) {
@@ -71,27 +65,43 @@ class ScrutinizerCoverageBase extends ScrutinizerBase {
}
class ScrutinizerCoverage extends ScrutinizerCoverageBase {
- static get route() {
- return {
- base: 'scrutinizer/coverage',
- pattern: ':vcs(g|b)/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/coverage',
+ pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'Scrutinizer coverage (GitHub/BitBucket)',
- pattern: ':vcs(g|b)/:user/:repo/:branch?',
- namedParams: {
- vcs: 'g',
- user: 'filp',
- repo: 'whoops',
- branch: 'master',
- },
- staticPreview: this.render({ coverage: 86 }),
+ static openApi = {
+ '/scrutinizer/coverage/{vcs}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer coverage (GitHub/Bitbucket)',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ ),
+ },
+ },
+ '/scrutinizer/coverage/{vcs}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer coverage (GitHub/Bitbucket) with branch',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
async handle({ vcs, user, repo, branch }) {
@@ -104,31 +114,37 @@ class ScrutinizerCoverage extends ScrutinizerCoverageBase {
}
class ScrutinizerCoverageGitLab extends ScrutinizerCoverageBase {
- static get route() {
- return {
- base: 'scrutinizer/coverage/gl',
- pattern: ':instance/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/coverage/gl',
+ pattern: ':instance/:user/:repo/:branch*',
}
- static get examples() {
- // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
- // The example used is valid, but the project will not be accessible if Shields users try to use it.
- // https://gitlab.propertywindow.nl/propertywindow/client
- // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
- return [
- {
- title: 'Scrutinizer coverage (GitLab)',
- pattern: ':instance/:user/:repo/:branch?',
- namedParams: {
- instance: 'propertywindow',
- user: 'propertywindow',
- repo: 'client',
- branch: 'master',
- },
- staticPreview: this.render({ coverage: 94 }),
+ // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
+ // The example used is valid, but the project will not be accessible if Shields users try to use it.
+ // https://gitlab.propertywindow.nl/propertywindow/client
+ // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
+ static openApi = {
+ '/scrutinizer/coverage/gl/{instance}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer coverage (GitLab)',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ ),
+ },
+ },
+ '/scrutinizer/coverage/gl/{instance}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer coverage (GitLab) with branch',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ { name: 'branch', example: 'master' },
+ ),
},
- ]
+ },
}
async handle({ instance, user, repo, branch }) {
@@ -141,11 +157,9 @@ class ScrutinizerCoverageGitLab extends ScrutinizerCoverageBase {
}
class ScrutinizerCoveragePlainGit extends ScrutinizerCoverageBase {
- static get route() {
- return {
- base: 'scrutinizer/coverage/gp',
- pattern: ':slug/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/coverage/gp',
+ pattern: ':slug/:branch*',
}
async handle({ slug, branch }) {
@@ -153,8 +167,8 @@ class ScrutinizerCoveragePlainGit extends ScrutinizerCoverageBase {
}
}
-module.exports = [
+export {
ScrutinizerCoverage,
ScrutinizerCoverageGitLab,
ScrutinizerCoveragePlainGit,
-]
+}
diff --git a/services/scrutinizer/scrutinizer-coverage.spec.js b/services/scrutinizer/scrutinizer-coverage.spec.js
index 1ff20e22b7fa2..e83768d8ce639 100644
--- a/services/scrutinizer/scrutinizer-coverage.spec.js
+++ b/services/scrutinizer/scrutinizer-coverage.spec.js
@@ -1,11 +1,9 @@
-'use strict'
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import { InvalidResponse, NotFound } from '../index.js'
+import { ScrutinizerCoverage } from './scrutinizer-coverage.service.js'
-const { expect } = require('chai')
-const { test, given } = require('sazerac')
-const { InvalidResponse, NotFound } = require('..')
-const [ScrutinizerCoverage] = require('./scrutinizer-coverage.service')
-
-describe('ScrutinizerCoverage', function() {
+describe('ScrutinizerCoverage', function () {
test(ScrutinizerCoverage.render, () => {
given({ coverage: 39 }).expect({
message: '39%',
@@ -25,8 +23,8 @@ describe('ScrutinizerCoverage', function() {
})
})
- context('transform()', function() {
- it('throws NotFound error when there is no coverage data', function() {
+ context('transform()', function () {
+ it('throws NotFound error when there is no coverage data', function () {
try {
ScrutinizerCoverage.prototype.transform({
branch: 'master',
@@ -50,7 +48,7 @@ describe('ScrutinizerCoverage', function() {
expect(e.prettyMessage).to.equal('coverage not found')
}
})
- it('throws InvalidResponse error when branch is missing statistics', function() {
+ it('throws InvalidResponse error when branch is missing statistics', function () {
expect(() =>
ScrutinizerCoverage.prototype.transform({
branch: 'gh-pages',
@@ -64,7 +62,7 @@ describe('ScrutinizerCoverage', function() {
},
},
},
- })
+ }),
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'metrics missing for branch')
diff --git a/services/scrutinizer/scrutinizer-coverage.tester.js b/services/scrutinizer/scrutinizer-coverage.tester.js
index bfe306b34d8a0..e23e7f59a3fbb 100644
--- a/services/scrutinizer/scrutinizer-coverage.tester.js
+++ b/services/scrutinizer/scrutinizer-coverage.tester.js
@@ -1,19 +1,15 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import { isIntegerPercentage } from '../test-validators.js'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'ScrutinizerCoverage',
title: 'ScrutinizerCoverage',
pathPrefix: '/scrutinizer/coverage',
-}))
+})
-t.create('code coverage (GitHub)')
- .get('/g/filp/whoops.json')
- .expectBadge({
- label: 'coverage',
- message: isIntegerPercentage,
- })
+t.create('code coverage (GitHub)').get('/g/filp/whoops.json').expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+})
t.create('code coverage branch (GitHub)')
.get('/g/PHPMailer/PHPMailer/master.json')
@@ -36,9 +32,7 @@ t.create('code coverage private project')
message: 'not authorized to access project',
})
-t.create('code coverage nonexistent project')
- .get('/gp/foo.json')
- .expectBadge({
- label: 'coverage',
- message: 'project not found',
- })
+t.create('code coverage nonexistent project').get('/gp/foo.json').expectBadge({
+ label: 'coverage',
+ message: 'project not found',
+})
diff --git a/services/scrutinizer/scrutinizer-quality.service.js b/services/scrutinizer/scrutinizer-quality.service.js
index 514351e529af9..51ae91c245409 100644
--- a/services/scrutinizer/scrutinizer-quality.service.js
+++ b/services/scrutinizer/scrutinizer-quality.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale } = require('../color-formatters')
-const ScrutinizerBase = require('./scrutinizer-base')
+import Joi from 'joi'
+import { colorScale } from '../color-formatters.js'
+import { pathParams } from '../index.js'
+import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
default_branch: Joi.string().required(),
@@ -19,25 +18,21 @@ const schema = Joi.object({
}).required(),
}).required(),
}),
- })
+ }),
)
.required(),
}).required()
const scale = colorScale(
[4, 5, 7, 9],
- ['red', 'orange', 'yellow', 'green', 'brightgreen']
+ ['red', 'orange', 'yellow', 'green', 'brightgreen'],
)
class ScrutinizerQualityBase extends ScrutinizerBase {
- static get category() {
- return 'analysis'
- }
+ static category = 'analysis'
- static get defaultBadgeData() {
- return {
- label: 'code quality',
- }
+ static defaultBadgeData = {
+ label: 'code quality',
}
static render({ score }) {
@@ -59,27 +54,43 @@ class ScrutinizerQualityBase extends ScrutinizerBase {
}
class ScrutinizerQuality extends ScrutinizerQualityBase {
- static get route() {
- return {
- base: 'scrutinizer/quality',
- pattern: ':vcs(g|b)/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/quality',
+ pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
- static get examples() {
- return [
- {
- title: 'Scrutinizer code quality (GitHub/Bitbucket)',
- pattern: ':vcs(g|b)/:user/:repo/:branch?',
- namedParams: {
- vcs: 'g',
- user: 'filp',
- repo: 'whoops',
- branch: 'master',
- },
- staticPreview: this.render({ score: 8.26 }),
+ static openApi = {
+ '/scrutinizer/quality/{vcs}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer quality (GitHub/Bitbucket)',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ ),
},
- ]
+ },
+ '/scrutinizer/quality/{vcs}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer quality (GitHub/Bitbucket) with branch',
+ parameters: pathParams(
+ {
+ name: 'vcs',
+ example: 'g',
+ description: 'Platform: Either GitHub or Bitbucket',
+ schema: { type: 'string', enum: this.getEnum('vcs') },
+ },
+ { name: 'user', example: 'filp' },
+ { name: 'repo', example: 'whoops' },
+ { name: 'branch', example: 'master' },
+ ),
+ },
+ },
}
async handle({ vcs, user, repo, branch }) {
@@ -92,31 +103,37 @@ class ScrutinizerQuality extends ScrutinizerQualityBase {
}
class ScrutinizerQualityGitLab extends ScrutinizerQualityBase {
- static get route() {
- return {
- base: 'scrutinizer/quality/gl',
- pattern: ':instance/:user/:repo/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/quality/gl',
+ pattern: ':instance/:user/:repo/:branch*',
}
- static get examples() {
- // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
- // The example used is valid, but the project will not be accessible if Shields users try to use it.
- // https://gitlab.propertywindow.nl/propertywindow/client
- // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
- return [
- {
- title: 'Scrutinizer coverage (GitLab)',
- pattern: ':instance/:user/:repo/:branch?',
- namedParams: {
- instance: 'propertywindow',
- user: 'propertywindow',
- repo: 'client',
- branch: 'master',
- },
- staticPreview: this.render({ score: 10.0 }),
+ // There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
+ // The example used is valid, but the project will not be accessible if Shields users try to use it.
+ // https://gitlab.propertywindow.nl/propertywindow/client
+ // https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
+ static openApi = {
+ '/scrutinizer/quality/gl/{instance}/{user}/{repo}': {
+ get: {
+ summary: 'Scrutinizer quality (GitLab)',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ ),
},
- ]
+ },
+ '/scrutinizer/quality/gl/{instance}/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Scrutinizer quality (GitLab) with branch',
+ parameters: pathParams(
+ { name: 'instance', example: 'propertywindow' },
+ { name: 'user', example: 'propertywindow' },
+ { name: 'repo', example: 'client' },
+ { name: 'branch', example: 'master' },
+ ),
+ },
+ },
}
async handle({ instance, user, repo, branch }) {
@@ -129,11 +146,9 @@ class ScrutinizerQualityGitLab extends ScrutinizerQualityBase {
}
class ScrutinizerQualityPlainGit extends ScrutinizerQualityBase {
- static get route() {
- return {
- base: 'scrutinizer/quality/gp',
- pattern: ':slug/:branch*',
- }
+ static route = {
+ base: 'scrutinizer/quality/gp',
+ pattern: ':slug/:branch*',
}
async handle({ slug, branch }) {
@@ -141,7 +156,7 @@ class ScrutinizerQualityPlainGit extends ScrutinizerQualityBase {
}
}
-module.exports = [
+export default [
ScrutinizerQuality,
ScrutinizerQualityGitLab,
ScrutinizerQualityPlainGit,
diff --git a/services/scrutinizer/scrutinizer-quality.tester.js b/services/scrutinizer/scrutinizer-quality.tester.js
index e1e685f3ce123..e4dfbe1e2f2cb 100644
--- a/services/scrutinizer/scrutinizer-quality.tester.js
+++ b/services/scrutinizer/scrutinizer-quality.tester.js
@@ -1,21 +1,17 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+export const t = new ServiceTester({
id: 'ScrutinizerQuality',
title: 'ScrutinizerQuality',
pathPrefix: '/scrutinizer/quality',
-}))
+})
const isQualityNumber = Joi.number().positive()
-t.create('code quality (GitHub)')
- .get('/g/filp/whoops.json')
- .expectBadge({
- label: 'code quality',
- message: isQualityNumber,
- })
+t.create('code quality (GitHub)').get('/g/filp/whoops.json').expectBadge({
+ label: 'code quality',
+ message: isQualityNumber,
+})
t.create('code quality branch (GitHub)')
.get('/g/PHPMailer/PHPMailer/master.json')
@@ -38,12 +34,10 @@ t.create('code quality private project')
message: 'not authorized to access project',
})
-t.create('code quality nonexistent project')
- .get('/gp/foo.json')
- .expectBadge({
- label: 'code quality',
- message: 'project not found',
- })
+t.create('code quality nonexistent project').get('/gp/foo.json').expectBadge({
+ label: 'code quality',
+ message: 'project not found',
+})
t.create('code quality data missing for default branch')
.get('/g/filp/whoops.json')
@@ -65,7 +59,7 @@ t.create('code quality data missing for default branch')
},
},
},
- })
+ }),
)
.expectBadge({
label: 'code quality',
diff --git a/services/scrutinizer/scrutinizer-redirect.service.js b/services/scrutinizer/scrutinizer-redirect.service.js
index 4aee6a5ebfa0c..95a97967c0d54 100644
--- a/services/scrutinizer/scrutinizer-redirect.service.js
+++ b/services/scrutinizer/scrutinizer-redirect.service.js
@@ -1,13 +1,11 @@
-'use strict'
-
-const { redirector } = require('..')
+import { redirector } from '../index.js'
const commonAttrs = {
category: 'analysis',
dateAdded: new Date('2019-04-24'),
}
-module.exports = [
+export default [
redirector({
route: {
base: 'scrutinizer',
diff --git a/services/scrutinizer/scrutinizer-redirect.tester.js b/services/scrutinizer/scrutinizer-redirect.tester.js
index c9ef3fbd569fc..d13a38bd2a547 100644
--- a/services/scrutinizer/scrutinizer-redirect.tester.js
+++ b/services/scrutinizer/scrutinizer-redirect.tester.js
@@ -1,12 +1,10 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'ScrutinizerQualityRedirect',
title: 'ScrutinizerQualityRedirect',
pathPrefix: '/scrutinizer',
-}))
+})
t.create('scrutinizer quality GitHub')
.get('/g/doctrine/orm.svg')
@@ -23,7 +21,7 @@ t.create('scrutinizer quality Bitbucket')
t.create('scrutinizer quality Bitbucket (branch)')
.get('/b/atlassian/python-bitbucket/develop.svg')
.expectRedirect(
- '/scrutinizer/quality/b/atlassian/python-bitbucket/develop.svg'
+ '/scrutinizer/quality/b/atlassian/python-bitbucket/develop.svg',
)
t.create('scrutinizer quality GitLab')
diff --git a/services/sdkman/sdkman-version.service.js b/services/sdkman/sdkman-version.service.js
new file mode 100644
index 0000000000000..796808b2b4cad
--- /dev/null
+++ b/services/sdkman/sdkman-version.service.js
@@ -0,0 +1,41 @@
+import { renderVersionBadge } from '../version.js'
+import { BaseService, InvalidResponse, pathParams } from '../index.js'
+
+export default class SdkmanVersion extends BaseService {
+ static category = 'version'
+
+ static route = {
+ base: 'sdkman/v',
+ pattern: ':candidate',
+ }
+
+ static openApi = {
+ '/sdkman/v/{candidate}': {
+ get: {
+ summary: 'SDKMAN Version',
+ parameters: pathParams({
+ name: 'candidate',
+ example: 'java',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'sdkman' }
+
+ async fetch({ candidate }) {
+ return this._request({
+ url: `https://api.sdkman.io/2/candidates/default/${candidate}`,
+ httpErrors: { 400: 'not found' },
+ })
+ }
+
+ async handle({ candidate }) {
+ const { buffer } = await this.fetch({ candidate })
+ const version = buffer.trim()
+ if (!version) {
+ throw new InvalidResponse({ prettyMessage: 'invalid response data' })
+ }
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/sdkman/sdkman-version.tester.js b/services/sdkman/sdkman-version.tester.js
new file mode 100644
index 0000000000000..362f009550b61
--- /dev/null
+++ b/services/sdkman/sdkman-version.tester.js
@@ -0,0 +1,22 @@
+import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('version (valid)').get('/java.json').expectBadge({
+ label: 'sdkman',
+ message: isVPlusDottedVersionNClausesWithOptionalSuffix,
+})
+
+t.create('version (not found)')
+ .get('/not-a-real-sdk.json')
+ .expectBadge({ label: 'sdkman', message: 'not found' })
+
+t.create('version (empty response)')
+ .get('/java.json')
+ .intercept(nock =>
+ nock('https://api.sdkman.io')
+ .get('/2/candidates/default/java')
+ .reply(200, ''),
+ )
+ .expectBadge({ label: 'sdkman', message: 'invalid response data' })
diff --git a/services/security-headers/security-headers.service.js b/services/security-headers/security-headers.service.js
index f3776bdf25923..91109400c125e 100644
--- a/services/security-headers/security-headers.service.js
+++ b/services/security-headers/security-headers.service.js
@@ -1,93 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseService, NotFound } = require('..')
-
-const queryParamSchema = Joi.object({
- url: optionalUrl.required(),
-}).required()
-
-const documentation = `
-
- The Security Headers
- provide an easy mechanism to analyze HTTP response headers and
- give information on how to deploy missing headers.
-
-
- The scan result will be hidden from the public result list and follow redirects will be on too.
-
-`
-
-module.exports = class SecurityHeaders extends BaseService {
- static get category() {
- return 'monitoring'
- }
-
- static get route() {
- return {
- base: '',
- pattern: 'security-headers',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Security Headers',
- namedParams: {},
- queryParams: { url: 'https://shields.io' },
- staticPreview: this.render({
- grade: 'A+',
- }),
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'security headers',
- }
- }
-
- static render({ grade }) {
- const colorMap = {
- 'A+': 'brightgreen',
- A: 'green',
- B: 'yellow',
- C: 'yellow',
- D: 'orange',
- E: 'orange',
- F: 'red',
- }
-
- return {
- message: grade,
- color: colorMap[grade],
- }
- }
-
- async handle(namedParams, { url }) {
- const { res } = await this._request({
- url: `https://securityheaders.com`,
- options: {
- method: 'HEAD',
- qs: {
- q: url,
- hide: 'on',
- followRedirects: 'on',
- },
- },
- })
-
- const grade = res.headers['x-grade']
-
- if (!grade) {
- throw new NotFound({ prettyMessage: 'not available' })
- }
-
- return this.constructor.render({ grade })
- }
-}
+import { retiredService } from '../index.js'
+
+export const SecurityHeaders = retiredService({
+ category: 'monitoring',
+ route: {
+ base: 'security-headers',
+ pattern: ':various+',
+ },
+ label: 'securityheaders',
+ dateAdded: new Date('2025-11-08'),
+})
diff --git a/services/security-headers/security-headers.tester.js b/services/security-headers/security-headers.tester.js
index 2fa884e23e25d..42d35f3e5711d 100644
--- a/services/security-headers/security-headers.tester.js
+++ b/services/security-headers/security-headers.tester.js
@@ -1,7 +1,11 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = new ServiceTester({
+ id: 'security-headers',
+ title: 'SecurityHeaders',
+})
-t.create('grade of http://shields.io')
- .get('/security-headers.json?url=https://shields.io')
- .expectBadge({ label: 'security headers', message: 'F', color: 'red' })
+t.create('deprecated service').get('/security-headers.json').expectBadge({
+ label: 'securityheaders',
+ message: 'retired badge',
+})
diff --git a/services/shippable/shippable.service.js b/services/shippable/shippable.service.js
deleted file mode 100644
index 3dc79baf56c9d..0000000000000
--- a/services/shippable/shippable.service.js
+++ /dev/null
@@ -1,85 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService, NotFound } = require('..')
-
-// source: https://github.com/badges/shields/pull/1362#discussion_r161693830
-const statusCodes = {
- 0: 'waiting',
- 10: 'queued',
- 20: 'processing',
- 30: 'success',
- 40: 'skipped',
- 50: 'unstable',
- 60: 'timeout',
- 70: 'cancelled',
- 80: 'failed',
- 90: 'stopped',
-}
-
-const schema = Joi.array()
- .items(
- Joi.object({
- branchName: Joi.string().required(),
- statusCode: Joi.number()
- .valid(...Object.keys(statusCodes).map(key => parseInt(key)))
- .required(),
- }).required()
- )
- .required()
-
-module.exports = class Shippable extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'shippable',
- pattern: ':projectId/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Shippable',
- pattern: ':projectId',
- namedParams: { projectId: '5444c5ecb904a4b21567b0ff' },
- staticPreview: this.render({ code: 30 }),
- },
- {
- title: 'Shippable branch',
- pattern: ':projectId/:branch',
- namedParams: {
- projectId: '5444c5ecb904a4b21567b0ff',
- branch: 'master',
- },
- staticPreview: this.render({ code: 30 }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'shippable' }
- }
-
- static render({ code }) {
- return renderBuildStatusBadge({ label: 'build', status: statusCodes[code] })
- }
-
- async fetch({ projectId }) {
- const url = `https://api.shippable.com/projects/${projectId}/branchRunStatus`
- return this._requestJson({ schema, url })
- }
-
- async handle({ projectId, branch = 'master' }) {
- const data = await this.fetch({ projectId })
- const builds = data.filter(result => result.branchName === branch)
- if (builds.length === 0) {
- throw new NotFound({ prettyMessage: 'branch not found' })
- }
- return this.constructor.render({ code: builds[0].statusCode })
- }
-}
diff --git a/services/shippable/shippable.tester.js b/services/shippable/shippable.tester.js
deleted file mode 100644
index 3f072dc28fbe6..0000000000000
--- a/services/shippable/shippable.tester.js
+++ /dev/null
@@ -1,35 +0,0 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('build status (valid, without branch)')
- .get('/5444c5ecb904a4b21567b0ff.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
-
-t.create('build status (valid, with branch)')
- .get('/5444c5ecb904a4b21567b0ff/master.json')
- .expectBadge({
- label: 'build',
- message: isBuildStatus,
- })
-
-t.create('build status (branch not found)')
- .get('/5444c5ecb904a4b21567b0ff/not-a-branch.json')
- .expectBadge({ label: 'shippable', message: 'branch not found' })
-
-t.create('build status (not found)')
- .get('/not-a-build.json')
- .expectBadge({ label: 'shippable', message: 'not found' })
-
-t.create('build status (unexpected status code)')
- .get('/5444c5ecb904a4b21567b0ff.json')
- .intercept(nock =>
- nock('https://api.shippable.com/')
- .get('/projects/5444c5ecb904a4b21567b0ff/branchRunStatus')
- .reply(200, '[{ "branchName": "master", "statusCode": 63 }]')
- )
- .expectBadge({ label: 'shippable', message: 'invalid response data' })
diff --git a/services/size.js b/services/size.js
new file mode 100644
index 0000000000000..3ac6eb9f0a064
--- /dev/null
+++ b/services/size.js
@@ -0,0 +1,27 @@
+/**
+ * Common functions and utilities for tasks related to file sizes.
+ *
+ * @module
+ */
+
+import byteSize from 'byte-size'
+
+/**
+ * Creates a badge object that displays information about a size in bytes number.
+ * It should usually be used to output a size badge.
+ *
+ * @param {number} bytes - Raw number of bytes to be formatted
+ * @param {'metric'|'iec'} units - Either 'metric' (multiples of 1000) or 'iec' (multiples of 1024).
+ * This should align with how the upstream displays sizes.
+ * @param {string} [label='size'] - Custom label
+ * @returns {object} A badge object that has three properties: label, message, and color
+ */
+function renderSizeBadge(bytes, units, label = 'size') {
+ return {
+ label,
+ message: byteSize(bytes, { units }).toString(),
+ color: 'blue',
+ }
+}
+
+export { renderSizeBadge }
diff --git a/services/snap-ci/snap-ci.service.js b/services/snap-ci/snap-ci.service.js
deleted file mode 100644
index 4b3cf224dc325..0000000000000
--- a/services/snap-ci/snap-ci.service.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-const commonAttrs = {
- category: 'build',
- label: 'snap ci',
- dateAdded: new Date('2018-01-23'),
-}
-
-module.exports = [
- deprecatedService({
- route: {
- base: 'snap',
- pattern: ':various*',
- },
- ...commonAttrs,
- }),
- deprecatedService({
- route: {
- base: 'snap-ci',
- pattern: ':various*',
- },
- ...commonAttrs,
- }),
-]
diff --git a/services/snap-ci/snap-ci.tester.js b/services/snap-ci/snap-ci.tester.js
deleted file mode 100644
index fb9659ee3e17b..0000000000000
--- a/services/snap-ci/snap-ci.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({ id: 'snap-ci', title: 'Snap CI' })
-module.exports = t
-
-t.create('no longer available (previously build state)')
- .get('/snap-ci/ThoughtWorksStudios/eb_deployer/master.json')
- .expectBadge({
- label: 'snap ci',
- message: 'no longer available',
- })
diff --git a/services/snapcraft/snapcraft-base.js b/services/snapcraft/snapcraft-base.js
new file mode 100644
index 0000000000000..ba1a72473095e
--- /dev/null
+++ b/services/snapcraft/snapcraft-base.js
@@ -0,0 +1,23 @@
+import { BaseJsonService, pathParam } from '../index.js'
+
+export const snapcraftPackageParam = pathParam({
+ name: 'package',
+ example: 'redis',
+})
+
+export const snapcraftBaseParams = [snapcraftPackageParam]
+
+const snapcraftBaseUrl = 'https://api.snapcraft.io/v2/snaps/info'
+
+export default class SnapcraftBase extends BaseJsonService {
+ async fetch(schema, { packageName }) {
+ return await this._requestJson({
+ schema,
+ url: `${snapcraftBaseUrl}/${packageName}`,
+ options: {
+ headers: { 'Snap-Device-Series': 16 },
+ },
+ httpErrors: { 404: 'package not found' },
+ })
+ }
+}
diff --git a/services/snapcraft/snapcraft-last-update.service.js b/services/snapcraft/snapcraft-last-update.service.js
new file mode 100644
index 0000000000000..64f55f54fdd4d
--- /dev/null
+++ b/services/snapcraft/snapcraft-last-update.service.js
@@ -0,0 +1,95 @@
+import Joi from 'joi'
+import { pathParams, queryParam, NotFound } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import SnapcraftBase, { snapcraftPackageParam } from './snapcraft-base.js'
+
+const queryParamSchema = Joi.object({
+ arch: Joi.string(),
+})
+
+const lastUpdateSchema = Joi.object({
+ 'channel-map': Joi.array()
+ .items(
+ Joi.object({
+ channel: Joi.object({
+ architecture: Joi.string().required(),
+ risk: Joi.string().required(),
+ track: Joi.string().required(),
+ 'released-at': Joi.string().required(),
+ }),
+ }).required(),
+ )
+ .min(1)
+ .required(),
+}).required()
+
+export default class SnapcraftLastUpdate extends SnapcraftBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'snapcraft/last-update',
+ pattern: ':package/:track/:risk',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/snapcraft/last-update/{package}/{track}/{risk}': {
+ get: {
+ summary: 'Snapcraft Last Update',
+ parameters: [
+ snapcraftPackageParam,
+ ...pathParams(
+ { name: 'track', example: 'latest' },
+ { name: 'risk', example: 'stable' },
+ ),
+ queryParam({
+ name: 'arch',
+ example: 'amd64',
+ description:
+ 'Architecture, when not specified, this will default to `amd64`.',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ static transform(apiData, track, risk, arch) {
+ const channelMap = apiData['channel-map']
+ let filteredChannelMap = channelMap.filter(
+ ({ channel }) => channel.architecture === arch,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'arch not found' })
+ }
+ filteredChannelMap = filteredChannelMap.filter(
+ ({ channel }) => channel.track === track,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'track not found' })
+ }
+ filteredChannelMap = filteredChannelMap.filter(
+ ({ channel }) => channel.risk === risk,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'risk not found' })
+ }
+
+ return filteredChannelMap[0]
+ }
+
+ async handle({ package: packageName, track, risk }, { arch = 'amd64' }) {
+ const parsedData = await this.fetch(lastUpdateSchema, { packageName })
+
+ // filter results by track, risk and arch
+ const { channel } = this.constructor.transform(
+ parsedData,
+ track,
+ risk,
+ arch,
+ )
+
+ return renderDateBadge(channel['released-at'])
+ }
+}
diff --git a/services/snapcraft/snapcraft-last-update.tester.js b/services/snapcraft/snapcraft-last-update.tester.js
new file mode 100644
index 0000000000000..b9451be6f1630
--- /dev/null
+++ b/services/snapcraft/snapcraft-last-update.tester.js
@@ -0,0 +1,46 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('last update for redis/latest/stable')
+ .get('/redis/latest/stable.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last update for redis/latest/stable and query param arch=arm64')
+ .get('/redis/latest/stable.json?arch=arm64')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('last update when package not found')
+ .get('/fake_package/fake/fake.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'package not found',
+ })
+
+t.create('last update for redis and invalid track')
+ .get('/redis/notFound/stable.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'track not found',
+ })
+
+t.create('last update for redis/latest and invalid risk')
+ .get('/redis/latest/notFound.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'risk not found',
+ })
+
+t.create('last update for redis/latest/stable and invalid arch (query param)')
+ .get('/redis/latest/stable.json?arch=fake')
+ .expectBadge({
+ label: 'last updated',
+ message: 'arch not found',
+ })
diff --git a/services/snapcraft/snapcraft-licence.service.js b/services/snapcraft/snapcraft-licence.service.js
new file mode 100644
index 0000000000000..3be42f1a92260
--- /dev/null
+++ b/services/snapcraft/snapcraft-licence.service.js
@@ -0,0 +1,41 @@
+import Joi from 'joi'
+import { renderLicenseBadge } from '../licenses.js'
+import SnapcraftBase, { snapcraftPackageParam } from './snapcraft-base.js'
+
+const licenseSchema = Joi.object({
+ snap: Joi.object({
+ license: Joi.string().required(),
+ }).required(),
+}).required()
+
+export default class SnapcraftLicense extends SnapcraftBase {
+ static category = 'license'
+
+ static route = {
+ base: 'snapcraft/l',
+ pattern: ':package',
+ }
+
+ static openApi = {
+ '/snapcraft/l/{package}': {
+ get: {
+ summary: 'Snapcraft License',
+ parameters: [snapcraftPackageParam],
+ },
+ },
+ }
+
+ static render({ license }) {
+ return renderLicenseBadge({ license })
+ }
+
+ static transform(apiData) {
+ return apiData.snap.license
+ }
+
+ async handle({ package: packageName }) {
+ const parsedData = await this.fetch(licenseSchema, { packageName })
+ const license = this.constructor.transform(parsedData)
+ return this.constructor.render({ license })
+ }
+}
diff --git a/services/snapcraft/snapcraft-licence.spec.js b/services/snapcraft/snapcraft-licence.spec.js
new file mode 100644
index 0000000000000..91f1247f34c8c
--- /dev/null
+++ b/services/snapcraft/snapcraft-licence.spec.js
@@ -0,0 +1,14 @@
+import { test, given } from 'sazerac'
+import SnapcraftLicense from './snapcraft-licence.service.js'
+
+describe('SnapcraftLicense', function () {
+ const testApiData = {
+ snap: {
+ license: 'BSD-3-Clause',
+ },
+ }
+
+ test(SnapcraftLicense.transform, () => {
+ given(testApiData).expect('BSD-3-Clause')
+ })
+})
diff --git a/services/snapcraft/snapcraft-licence.tester.js b/services/snapcraft/snapcraft-licence.tester.js
new file mode 100644
index 0000000000000..a2217ba5955ab
--- /dev/null
+++ b/services/snapcraft/snapcraft-licence.tester.js
@@ -0,0 +1,14 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Snapcraft license (valid)').get('/redis.json').expectBadge({
+ label: 'license',
+ message: 'BSD-3-Clause',
+})
+
+t.create('Snapcraft license(invalid)')
+ .get('/this_package_doesnt_exist.json')
+ .expectBadge({
+ label: 'license',
+ message: 'package not found',
+ })
diff --git a/services/snapcraft/snapcraft-version.service.js b/services/snapcraft/snapcraft-version.service.js
new file mode 100644
index 0000000000000..77639fee66028
--- /dev/null
+++ b/services/snapcraft/snapcraft-version.service.js
@@ -0,0 +1,98 @@
+import Joi from 'joi'
+import { NotFound, pathParams, queryParam } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import SnapcraftBase, { snapcraftPackageParam } from './snapcraft-base.js'
+
+const queryParamSchema = Joi.object({
+ arch: Joi.string(),
+})
+
+const versionSchema = Joi.object({
+ 'channel-map': Joi.array()
+ .items(
+ Joi.object({
+ channel: Joi.object({
+ architecture: Joi.string().required(),
+ risk: Joi.string().required(),
+ track: Joi.string().required(),
+ }),
+ version: Joi.string().required(),
+ }).required(),
+ )
+ .min(1)
+ .required(),
+}).required()
+
+export default class SnapcraftVersion extends SnapcraftBase {
+ static category = 'version'
+
+ static route = {
+ base: 'snapcraft/v',
+ pattern: ':package/:track/:risk',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/snapcraft/v/{package}/{track}/{risk}': {
+ get: {
+ summary: 'Snapcraft Version',
+ parameters: [
+ snapcraftPackageParam,
+ ...pathParams(
+ { name: 'track', example: 'latest' },
+ { name: 'risk', example: 'stable' },
+ ),
+ queryParam({
+ name: 'arch',
+ example: 'amd64',
+ description:
+ 'Architecture, When not specified, this will default to `amd64`.',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'snapcraft' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ static transform(apiData, track, risk, arch) {
+ const channelMap = apiData['channel-map']
+ let filteredChannelMap = channelMap.filter(
+ ({ channel }) => channel.architecture === arch,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'arch not found' })
+ }
+ filteredChannelMap = filteredChannelMap.filter(
+ ({ channel }) => channel.track === track,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'track not found' })
+ }
+ filteredChannelMap = filteredChannelMap.filter(
+ ({ channel }) => channel.risk === risk,
+ )
+ if (filteredChannelMap.length === 0) {
+ throw new NotFound({ prettyMessage: 'risk not found' })
+ }
+
+ return filteredChannelMap[0]
+ }
+
+ async handle({ package: packageName, track, risk }, { arch = 'amd64' }) {
+ const parsedData = await this.fetch(versionSchema, { packageName })
+
+ // filter results by track, risk and arch
+ const { version } = this.constructor.transform(
+ parsedData,
+ track,
+ risk,
+ arch,
+ )
+ return this.constructor.render({ version })
+ }
+}
diff --git a/services/snapcraft/snapcraft-version.spec.js b/services/snapcraft/snapcraft-version.spec.js
new file mode 100644
index 0000000000000..9944422eb75ee
--- /dev/null
+++ b/services/snapcraft/snapcraft-version.spec.js
@@ -0,0 +1,103 @@
+import { expect } from 'chai'
+import { test, given } from 'sazerac'
+import _ from 'lodash'
+import { NotFound } from '../index.js'
+import SnapcraftVersion from './snapcraft-version.service.js'
+
+describe('SnapcraftVersion', function () {
+ const exampleChannel = {
+ channel: {
+ architecture: 'amd64',
+ risk: 'stable',
+ track: 'latest',
+ },
+ version: '1.2.3',
+ }
+ const exampleArchChange = _.merge(_.cloneDeep(exampleChannel), {
+ channel: { architecture: 'arm64' },
+ version: '2.3.4',
+ })
+ const exampleTrackChange = _.merge(_.cloneDeep(exampleChannel), {
+ channel: { track: 'beta' },
+ version: '3.4.5',
+ })
+ const exampleRiskChange = _.merge(_.cloneDeep(exampleChannel), {
+ channel: { risk: 'edge' },
+ version: '5.4.6',
+ })
+ const testApiData = {
+ 'channel-map': [
+ exampleChannel,
+ exampleArchChange,
+ exampleTrackChange,
+ exampleRiskChange,
+ ],
+ }
+
+ test(SnapcraftVersion.transform, () => {
+ given(
+ testApiData,
+ exampleChannel.channel.track,
+ exampleChannel.channel.risk,
+ exampleChannel.channel.architecture,
+ ).expect(exampleChannel)
+ // change arch
+ given(
+ testApiData,
+ exampleChannel.channel.track,
+ exampleChannel.channel.risk,
+ exampleArchChange.channel.architecture,
+ ).expect(exampleArchChange)
+ // change track
+ given(
+ testApiData,
+ exampleTrackChange.channel.track,
+ exampleChannel.channel.risk,
+ exampleChannel.channel.architecture,
+ ).expect(exampleTrackChange)
+ // change risk
+ given(
+ testApiData,
+ exampleChannel.channel.track,
+ exampleRiskChange.channel.risk,
+ exampleChannel.channel.architecture,
+ ).expect(exampleRiskChange)
+ })
+
+ it('throws NotFound error with missing arch', function () {
+ expect(() => {
+ SnapcraftVersion.transform(
+ testApiData,
+ exampleChannel.channel.track,
+ exampleChannel.channel.risk,
+ 'missing',
+ )
+ })
+ .to.throw(NotFound)
+ .with.property('prettyMessage', 'arch not found')
+ })
+ it('throws NotFound error with missing track', function () {
+ expect(() => {
+ SnapcraftVersion.transform(
+ testApiData,
+ 'missing',
+ exampleChannel.channel.risk,
+ exampleChannel.channel.architecture,
+ )
+ })
+ .to.throw(NotFound)
+ .with.property('prettyMessage', 'track not found')
+ })
+ it('throws NotFound error with missing risk', function () {
+ expect(() => {
+ SnapcraftVersion.transform(
+ testApiData,
+ exampleChannel.channel.track,
+ 'missing',
+ exampleChannel.channel.architecture,
+ )
+ })
+ .to.throw(NotFound)
+ .with.property('prettyMessage', 'risk not found')
+ })
+})
diff --git a/services/snapcraft/snapcraft-version.tester.js b/services/snapcraft/snapcraft-version.tester.js
new file mode 100644
index 0000000000000..dc4a5001b66e4
--- /dev/null
+++ b/services/snapcraft/snapcraft-version.tester.js
@@ -0,0 +1,45 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Snapcraft Version for redis')
+ .get('/redis/latest/stable.json')
+ .expectBadge({
+ label: 'snapcraft',
+ message: isSemver,
+ })
+
+t.create('Snapcraft Version for redis (query param arch=arm64)')
+ .get('/redis/latest/stable.json?arch=arm64')
+ .expectBadge({
+ label: 'snapcraft',
+ message: isSemver,
+ })
+
+t.create('Snapcraft Version for redis (invalid package)')
+ .get('/this_package_doesnt_exist/fake/fake.json')
+ .expectBadge({
+ label: 'snapcraft',
+ message: 'package not found',
+ })
+
+t.create('Snapcraft Version for redis (invalid track)')
+ .get('/redis/notfound/stable.json')
+ .expectBadge({
+ label: 'snapcraft',
+ message: 'track not found',
+ })
+
+t.create('Snapcraft Version for redis (invalid risk)')
+ .get('/redis/latest/notfound.json')
+ .expectBadge({
+ label: 'snapcraft',
+ message: 'risk not found',
+ })
+
+t.create('Snapcraft Version for redis (invalid arch)')
+ .get('/redis/latest/stable.json?arch=fake')
+ .expectBadge({
+ label: 'snapcraft',
+ message: 'arch not found',
+ })
diff --git a/services/snyk/snyk-test-helpers.js b/services/snyk/snyk-test-helpers.js
deleted file mode 100644
index 3fea094774e10..0000000000000
--- a/services/snyk/snyk-test-helpers.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict'
-
-const zeroVulnerabilitiesSvg =
- 'vulnerabilities vulnerabilities 0 0 '
-const twoVulnerabilitiesSvg =
- 'vulnerabilities vulnerabilities 2 2 '
-
-module.exports = {
- zeroVulnerabilitiesSvg,
- twoVulnerabilitiesSvg,
-}
diff --git a/services/snyk/snyk-vulnerability-base.js b/services/snyk/snyk-vulnerability-base.js
deleted file mode 100644
index f6e8e0e889a18..0000000000000
--- a/services/snyk/snyk-vulnerability-base.js
+++ /dev/null
@@ -1,46 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseSvgScrapingService } = require('..')
-
-const schema = Joi.object({
- message: Joi.alternatives()
- .try(Joi.string().regex(/^\d*$/), Joi.equal('unknown'))
- .required(),
-}).required()
-
-module.exports = class SnykVulnerabilityBase extends BaseSvgScrapingService {
- static get category() {
- return 'analysis'
- }
-
- static get defaultBadgeData() {
- return {
- label: 'vulnerabilities',
- }
- }
-
- static render({ vulnerabilities }) {
- let color = 'red'
- if (vulnerabilities === '0') {
- color = 'brightgreen'
- }
- return {
- message: vulnerabilities,
- color,
- }
- }
-
- async fetch({ url, qs, errorMessages }) {
- const { message: vulnerabilities } = await this._requestSvg({
- url,
- schema,
- options: {
- qs,
- },
- errorMessages,
- })
-
- return { vulnerabilities }
- }
-}
diff --git a/services/snyk/snyk-vulnerability-github.service.js b/services/snyk/snyk-vulnerability-github.service.js
deleted file mode 100644
index ede8a0a503e17..0000000000000
--- a/services/snyk/snyk-vulnerability-github.service.js
+++ /dev/null
@@ -1,55 +0,0 @@
-'use strict'
-
-const SynkVulnerabilityBase = require('./snyk-vulnerability-base')
-
-module.exports = class SnykVulnerabilityGitHub extends SynkVulnerabilityBase {
- static get route() {
- return {
- base: 'snyk/vulnerabilities/github',
- pattern: ':user/:repo/:manifestFilePath*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Snyk Vulnerabilities for GitHub Repo',
- pattern: ':user/:repo',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- },
- staticPreview: this.render({ vulnerabilities: '0' }),
- },
- {
- title: 'Snyk Vulnerabilities for GitHub Repo (Specific Manifest)',
- pattern: ':user/:repo/:manifestFilePath',
- namedParams: {
- user: 'badges',
- repo: 'shields',
- manifestFilePath: 'badge-maker/package.json',
- },
- staticPreview: this.render({ vulnerabilities: '0' }),
- documentation: `
-
- Provide the path to your target manifest file relative to the base of your repository.
- Snyk does not support using a specific branch for this, so do not include "blob" nor a branch name.
-
- `,
- },
- ]
- }
-
- async handle({ user, repo, manifestFilePath }) {
- const url = `https://snyk.io/test/github/${user}/${repo}/badge.svg`
- const qs = { targetFile: manifestFilePath }
- const { vulnerabilities } = await this.fetch({
- url,
- qs,
- errorMessages: {
- 404: 'repo or manifest not found',
- },
- })
- return this.constructor.render({ vulnerabilities })
- }
-}
diff --git a/services/snyk/snyk-vulnerability-github.tester.js b/services/snyk/snyk-vulnerability-github.tester.js
deleted file mode 100644
index 23305db826bb1..0000000000000
--- a/services/snyk/snyk-vulnerability-github.tester.js
+++ /dev/null
@@ -1,98 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const {
- twoVulnerabilitiesSvg,
- zeroVulnerabilitiesSvg,
-} = require('./snyk-test-helpers')
-
-t.create('valid repo')
- .get('/snyk/snyk.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: Joi.number().required(),
- })
-
-t.create('non existent repo')
- .get('/badges/not-real.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: 'repo or manifest not found',
- })
-
-t.create('valid target manifest path')
- .get('/snyk/vulndb-fixtures/packages/cli/0.1.0/package.json.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: Joi.number().required(),
- })
-
-t.create('invalid target manifest path')
- .get('/badges/shields/badge-maker/requirements.txt.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: 'repo or manifest not found',
- })
-
-t.create('repo has no vulnerabilities')
- .get('/badges/shields.json')
- .intercept(nock =>
- nock('https://snyk.io/test/github/badges/shields')
- .get('/badge.svg')
- .reply(200, zeroVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '0',
- color: 'brightgreen',
- })
-
-t.create('repo has vulnerabilities')
- .get('/badges/shields.json')
- .intercept(nock =>
- nock('https://snyk.io/test/github/badges/shields')
- .get('/badge.svg')
- .reply(200, twoVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '2',
- color: 'red',
- })
-
-t.create('target manifest file has no vulnerabilities')
- .get('/badges/shields/badge-maker/package.json.json')
- .intercept(nock =>
- nock('https://snyk.io/test/github/badges/shields')
- .get('/badge.svg')
- .query({
- targetFile: 'badge-maker/package.json',
- })
- .reply(200, zeroVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '0',
- color: 'brightgreen',
- })
-
-t.create('target manifest file has vulnerabilities')
- .get('/badges/shields/badge-maker/package.json.json')
- .intercept(nock =>
- nock('https://snyk.io/test/github/badges/shields')
- .get('/badge.svg')
- .query({
- targetFile: 'badge-maker/package.json',
- })
- .reply(200, twoVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '2',
- color: 'red',
- })
diff --git a/services/snyk/snyk-vulnerability-npm.service.js b/services/snyk/snyk-vulnerability-npm.service.js
deleted file mode 100644
index 4e8d19dd32fc3..0000000000000
--- a/services/snyk/snyk-vulnerability-npm.service.js
+++ /dev/null
@@ -1,66 +0,0 @@
-'use strict'
-
-const { NotFound } = require('..')
-const SynkVulnerabilityBase = require('./snyk-vulnerability-base')
-
-module.exports = class SnykVulnerabilityNpm extends SynkVulnerabilityBase {
- static get route() {
- return {
- base: 'snyk/vulnerabilities/npm',
- pattern: ':packageName(.+?)',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Snyk Vulnerabilities for npm package',
- pattern: ':packageName',
- namedParams: {
- packageName: 'mocha',
- },
- staticPreview: this.render({ vulnerabilities: '0' }),
- },
- {
- title: 'Snyk Vulnerabilities for npm package version',
- pattern: ':packageName',
- namedParams: {
- packageName: 'mocha@4.0.0',
- },
- staticPreview: this.render({ vulnerabilities: '1' }),
- },
- {
- title: 'Snyk Vulnerabilities for npm scoped package',
- pattern: ':packageName',
- namedParams: {
- packageName: '@babel/core',
- },
- staticPreview: this.render({ vulnerabilities: '0' }),
- },
- ]
- }
-
- async handle({ packageName }) {
- const url = `https://snyk.io/test/npm/${packageName}/badge.svg`
-
- try {
- const { vulnerabilities } = await this.fetch({
- url,
- // Snyk returns an HTTP 200 with an HTML page when the specified
- // npm package is not found that contains the text 404.
- // Including this in case Snyk starts returning a 404 response code instead.
- errorMessages: {
- 404: 'npm package is invalid or does not exist',
- },
- })
- return this.constructor.render({ vulnerabilities })
- } catch (e) {
- // If the package is invalid/nonexistent Snyk will return an HTML page
- // which will result in an InvalidResponse error being thrown by the valueFromSvgBadge()
- // function. Catching it here to switch to a more contextualized error message.
- throw new NotFound({
- prettyMessage: 'npm package is invalid or does not exist',
- })
- }
- }
-}
diff --git a/services/snyk/snyk-vulnerability-npm.tester.js b/services/snyk/snyk-vulnerability-npm.tester.js
deleted file mode 100644
index 3ab9e42c3b728..0000000000000
--- a/services/snyk/snyk-vulnerability-npm.tester.js
+++ /dev/null
@@ -1,87 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const {
- twoVulnerabilitiesSvg,
- zeroVulnerabilitiesSvg,
-} = require('./snyk-test-helpers')
-
-t.create('valid package latest version')
- .get('/commander.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: Joi.number().required(),
- })
-
-t.create('valid scoped package latest version')
- .get('/@babel/core.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: Joi.number().required(),
- })
-
-t.create('non existent package')
- .get('/mochaabcdef.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: 'npm package is invalid or does not exist',
- })
-
-t.create('valid package specific version')
- .get('/commander@2.20.0.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: Joi.number().required(),
- })
-
-t.create('non existent package version')
- .get('/gh-badges@0.3.4.json')
- .timeout(20000)
- .expectBadge({
- label: 'vulnerabilities',
- message: 'npm package is invalid or does not exist',
- })
-
-t.create('package has no vulnerabilities')
- .get('/mocha.json')
- .intercept(nock =>
- nock('https://snyk.io/test/npm/mocha')
- .get('/badge.svg')
- .reply(200, zeroVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '0',
- color: 'brightgreen',
- })
-
-t.create('package has vulnerabilities')
- .get('/mocha.json')
- .intercept(nock =>
- nock('https://snyk.io/test/npm/mocha')
- .get('/badge.svg')
- .reply(200, twoVulnerabilitiesSvg)
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: '2',
- color: 'red',
- })
-
-t.create('package not found')
- .get('/not-mocha-fake-ish@13.0.0.json')
- .intercept(nock =>
- nock('https://snyk.io/test/npm/not-mocha-fake-ish@13.0.0')
- .get('/badge.svg')
- .reply(200, 'foo')
- )
- .expectBadge({
- label: 'vulnerabilities',
- message: 'npm package is invalid or does not exist',
- color: 'red',
- })
diff --git a/services/sonar/sonar-base.js b/services/sonar/sonar-base.js
index 2f6dd272374e1..09b8905204f0f 100644
--- a/services/sonar/sonar-base.js
+++ b/services/sonar/sonar-base.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService, NotFound } = require('..')
-const { isLegacyVersion } = require('./sonar-helpers')
+import Joi from 'joi'
+import { BaseJsonService, NotFound } from '../index.js'
+import { isLegacyVersion } from './sonar-helpers.js'
// It is possible to see HTTP 404 response codes and HTTP 200 responses
// with empty arrays of metric values, with both the legacy (pre v5.3) and modern APIs.
@@ -24,9 +22,9 @@ const modernSchema = Joi.object({
metric: Joi.string().required(),
value: Joi.alternatives(
Joi.number().min(0),
- Joi.allow('OK', 'ERROR')
+ Joi.equal('OK', 'ERROR'),
).required(),
- })
+ }),
)
.min(0)
.required(),
@@ -42,39 +40,42 @@ const legacySchema = Joi.array()
key: Joi.string().required(),
val: Joi.alternatives(
Joi.number().min(0),
- Joi.allow('OK', 'ERROR')
+ Joi.equal('OK', 'ERROR'),
).required(),
- })
+ }),
)
.required(),
- }).required()
+ }).required(),
)
.required()
-module.exports = class SonarBase extends BaseJsonService {
- static get auth() {
- return { userKey: 'sonarqube_token', serviceKey: 'sonar' }
- }
+export default class SonarBase extends BaseJsonService {
+ static auth = { userKey: 'sonarqube_token', serviceKey: 'sonar' }
- async fetch({ sonarVersion, server, component, metricName }) {
+ async fetch({ sonarVersion, server, component, metricName, branch }) {
const useLegacyApi = isLegacyVersion({ sonarVersion })
- let qs, url, schema
+ let searchParams, url, schema
if (useLegacyApi) {
schema = legacySchema
url = `${server}/api/resources`
- qs = {
+ searchParams = {
resource: component,
depth: 0,
metrics: metricName,
includeTrends: true,
+ branch,
}
} else {
schema = modernSchema
url = `${server}/api/measures/component`
- qs = {
- componentKey: component,
+ // componentKey query param was renamed in version 6.6
+ const componentKey =
+ parseFloat(sonarVersion) >= 6.6 ? 'component' : 'componentKey'
+ searchParams = {
+ [componentKey]: component,
metricKeys: metricName,
+ branch,
}
}
@@ -82,11 +83,11 @@ module.exports = class SonarBase extends BaseJsonService {
this.authHelper.withBasicAuth({
schema,
url,
- options: { qs },
- errorMessages: {
+ options: { searchParams },
+ httpErrors: {
404: 'component or metric not found, or legacy API not supported',
},
- })
+ }),
)
}
diff --git a/services/sonar/sonar-coverage.service.js b/services/sonar/sonar-coverage.service.js
index bfcee3672853b..a73bfa9f9de61 100644
--- a/services/sonar/sonar-coverage.service.js
+++ b/services/sonar/sonar-coverage.service.js
@@ -1,43 +1,46 @@
-'use strict'
+import { pathParam } from '../index.js'
+import { coveragePercentage } from '../color-formatters.js'
+import SonarBase from './sonar-base.js'
+import {
+ documentation,
+ queryParamSchema,
+ openApiQueryParams,
+} from './sonar-helpers.js'
-const { coveragePercentage } = require('../color-formatters')
-const SonarBase = require('./sonar-base')
-const { documentation, keywords, queryParamSchema } = require('./sonar-helpers')
+export default class SonarCoverage extends SonarBase {
+ static category = 'coverage'
-module.exports = class SonarCoverage extends SonarBase {
- static get category() {
- return 'coverage'
+ static route = {
+ base: 'sonar/coverage',
+ pattern: ':component/:branch*',
+ queryParamSchema,
}
- static get route() {
- return {
- base: 'sonar/coverage',
- pattern: ':component',
- queryParamSchema,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Sonar Coverage',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({ coverage: 63 }),
- keywords,
- documentation,
+ static openApi = {
+ '/sonar/coverage/{component}': {
+ get: {
+ summary: 'Sonar Coverage',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'gitify-app_gitify' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
+ '/sonar/coverage/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Coverage (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'gitify-app_gitify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'coverage' }
- }
+ static defaultBadgeData = { label: 'coverage' }
static render({ coverage }) {
return {
@@ -46,11 +49,12 @@ module.exports = class SonarCoverage extends SonarBase {
}
}
- async handle({ component }, { server, sonarVersion }) {
+ async handle({ component, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
metricName: 'coverage',
})
const { coverage } = this.transform({
diff --git a/services/sonar/sonar-coverage.spec.js b/services/sonar/sonar-coverage.spec.js
new file mode 100644
index 0000000000000..c6c2e1e5086b2
--- /dev/null
+++ b/services/sonar/sonar-coverage.spec.js
@@ -0,0 +1,19 @@
+import { testAuth } from '../test-helpers.js'
+import SonarCoverage from './sonar-coverage.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
+
+describe('SonarCoverage', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarCoverage,
+ 'BasicAuth',
+ legacySonarResponse('coverage', 95),
+ { configOverride: testAuthConfigOverride },
+ )
+ })
+ })
+})
diff --git a/services/sonar/sonar-coverage.tester.js b/services/sonar/sonar-coverage.tester.js
index feb31425c22fe..68fab4e88cd65 100644
--- a/services/sonar/sonar-coverage.tester.js
+++ b/services/sonar/sonar-coverage.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isIntegerPercentage } = require('../test-validators')
+import { createServiceTester } from '../tester.js'
+import { isIntegerPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
// The service tests targeting the legacy SonarQube API are mocked
// because of the lack of publicly accessible, self-hosted, legacy SonarQube instances
@@ -10,7 +9,14 @@ const { isIntegerPercentage } = require('../test-validators')
// for other service tests.
t.create('Coverage')
- .get('/swellaby%3Aletra.json?server=https://sonarcloud.io')
+ .get('/gitify-app_gitify.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'coverage',
+ message: isIntegerPercentage,
+ })
+
+t.create('Coverage (branch)')
+ .get('/gitify-app_gitify/main.json?server=https://sonarcloud.io')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
@@ -18,7 +24,7 @@ t.create('Coverage')
t.create('Coverage (legacy API supported)')
.get(
- '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -38,7 +44,7 @@ t.create('Coverage (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'coverage',
diff --git a/services/sonar/sonar-documented-api-density.service.js b/services/sonar/sonar-documented-api-density.service.js
index 9af383ffbd376..326a96aa5ee41 100644
--- a/services/sonar/sonar-documented-api-density.service.js
+++ b/services/sonar/sonar-documented-api-density.service.js
@@ -1,50 +1,51 @@
-'use strict'
-
-const SonarBase = require('./sonar-base')
-const {
+import { pathParam } from '../index.js'
+import SonarBase from './sonar-base.js'
+import {
queryParamSchema,
+ openApiQueryParams,
getLabel,
positiveMetricColorScale,
- keywords,
documentation,
-} = require('./sonar-helpers')
+} from './sonar-helpers.js'
const metric = 'public_documented_api_density'
-module.exports = class SonarDocumentedApiDensity extends SonarBase {
- static get category() {
- return 'analysis'
- }
+export default class SonarDocumentedApiDensity extends SonarBase {
+ static category = 'analysis'
- static get route() {
- return {
- base: `sonar/${metric}`,
- pattern: ':component',
- queryParamSchema,
- }
+ static route = {
+ base: `sonar/${metric}`,
+ pattern: ':component/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Sonar Documented API Density',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({ density: 82 }),
- keywords,
- documentation,
+ static get openApi() {
+ const routes = {}
+ routes[`/sonar/${metric}/{component}`] = {
+ get: {
+ summary: 'Sonar Documented API Density',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'brave_brave-core' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ }
+ routes[`/sonar/${metric}/{component}/{branch}`] = {
+ get: {
+ summary: 'Sonar Documented API Density (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
+ },
+ }
+ return routes
}
- static get defaultBadgeData() {
- return { label: getLabel({ metric }) }
- }
+ static defaultBadgeData = { label: getLabel({ metric }) }
static render({ density }) {
return {
@@ -53,11 +54,12 @@ module.exports = class SonarDocumentedApiDensity extends SonarBase {
}
}
- async handle({ component }, { server, sonarVersion }) {
+ async handle({ component, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
metricName: metric,
})
const metrics = this.transform({ json, sonarVersion })
diff --git a/services/sonar/sonar-documented-api-density.spec.js b/services/sonar/sonar-documented-api-density.spec.js
index e27a25dc1377c..d8b01594bb1b6 100644
--- a/services/sonar/sonar-documented-api-density.spec.js
+++ b/services/sonar/sonar-documented-api-density.spec.js
@@ -1,9 +1,12 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { testAuth } from '../test-helpers.js'
+import SonarDocumentedApiDensity from './sonar-documented-api-density.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
-const { test, given } = require('sazerac')
-const SonarDocumentedApiDensity = require('./sonar-documented-api-density.service')
-
-describe('SonarDocumentedApiDensity', function() {
+describe('SonarDocumentedApiDensity', function () {
test(SonarDocumentedApiDensity.render, () => {
given({ density: 0 }).expect({
message: '0%',
@@ -26,4 +29,15 @@ describe('SonarDocumentedApiDensity', function() {
color: 'brightgreen',
})
})
+
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarDocumentedApiDensity,
+ 'BasicAuth',
+ legacySonarResponse('density', 93),
+ { configOverride: testAuthConfigOverride },
+ )
+ })
+ })
})
diff --git a/services/sonar/sonar-documented-api-density.tester.js b/services/sonar/sonar-documented-api-density.tester.js
index b879d6f5b10b0..cde30fe3fc15b 100644
--- a/services/sonar/sonar-documented-api-density.tester.js
+++ b/services/sonar/sonar-documented-api-density.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// The service tests targeting the legacy SonarQube API are mocked
// because of the lack of publicly accessible, self-hosted, legacy SonarQube instances
@@ -13,9 +12,7 @@ const t = (module.exports = require('../tester').createServiceTester())
// https://docs.sonarqube.org/7.0/MetricDefinitions.html
// https://sonarcloud.io/api/measures/component?componentKey=org.sonarsource.sonarqube:sonarqube&metricKeys=public_documented_api_density
t.create('Documented API Density (not found)')
- .get(
- '/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io'
- )
+ .get('/brave_brave-core.json?server=https://sonarcloud.io')
.expectBadge({
label: 'public documented api density',
message: 'metric not found',
@@ -23,7 +20,7 @@ t.create('Documented API Density (not found)')
t.create('Documented API Density')
.get(
- '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.somewhatold.com&sonarVersion=6.1'
+ '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.somewhatold.com&sonarVersion=6.1',
)
.intercept(nock =>
nock('http://sonar.somewhatold.com/api')
@@ -41,7 +38,7 @@ t.create('Documented API Density')
},
],
},
- })
+ }),
)
.expectBadge({
label: 'public documented api density',
@@ -50,7 +47,7 @@ t.create('Documented API Density')
t.create('Documented API Density (legacy API supported)')
.get(
- '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -70,7 +67,7 @@ t.create('Documented API Density (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'public documented api density',
diff --git a/services/sonar/sonar-fortify-rating.service.js b/services/sonar/sonar-fortify-rating.service.js
index 3234bffbf41d4..e623ca4609574 100644
--- a/services/sonar/sonar-fortify-rating.service.js
+++ b/services/sonar/sonar-fortify-rating.service.js
@@ -1,7 +1,10 @@
-'use strict'
-
-const SonarBase = require('./sonar-base')
-const { queryParamSchema, keywords, documentation } = require('./sonar-helpers')
+import { pathParam } from '../index.js'
+import SonarBase from './sonar-base.js'
+import {
+ queryParamSchema,
+ openApiQueryParams,
+ documentation,
+} from './sonar-helpers.js'
const colorMap = {
0: 'red',
@@ -12,46 +15,47 @@ const colorMap = {
5: 'brightgreen',
}
-module.exports = class SonarFortifyRating extends SonarBase {
- static get category() {
- return 'analysis'
- }
+const description = `
+Note that the Fortify Security Rating badge will only work on Sonar instances that have the Fortify SonarQube Plugin installed.
+The badge is not available for projects analyzed on SonarCloud.io
- static get route() {
- return {
- base: 'sonar/fortify-security-rating',
- pattern: ':component',
- queryParamSchema,
- }
+${documentation}
+`
+
+export default class SonarFortifyRating extends SonarBase {
+ static category = 'analysis'
+
+ static route = {
+ base: 'sonar/fortify-security-rating',
+ pattern: ':component/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Sonar Fortify Security Rating',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({ rating: 4 }),
- keywords,
- documentation: `
-
- Note that the Fortify Security Rating badge will only work on Sonar instances that have the Fortify SonarQube Plugin installed.
- The badge is not available for projects analyzed on SonarCloud.io
-
- ${documentation}
- `,
+ static openApi = {
+ '/sonar/fortify-security-rating/{component}': {
+ get: {
+ summary: 'Sonar Fortify Security Rating',
+ description,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
+ '/sonar/fortify-security-rating/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Fortify Security Rating (branch)',
+ description,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
}
- static get defaultBadgeData() {
- return { label: 'fortify-security-rating' }
- }
+ static defaultBadgeData = { label: 'fortify-security-rating' }
static render({ rating }) {
return {
@@ -60,11 +64,12 @@ module.exports = class SonarFortifyRating extends SonarBase {
}
}
- async handle({ component }, { server, sonarVersion }) {
+ async handle({ component, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
metricName: 'fortify-security-rating',
})
diff --git a/services/sonar/sonar-fortify-rating.spec.js b/services/sonar/sonar-fortify-rating.spec.js
index 90b5b11a80b50..b226d64762015 100644
--- a/services/sonar/sonar-fortify-rating.spec.js
+++ b/services/sonar/sonar-fortify-rating.spec.js
@@ -1,53 +1,19 @@
-'use strict'
+import { testAuth } from '../test-helpers.js'
+import SonarFortifyRating from './sonar-fortify-rating.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const SonarFortifyRating = require('./sonar-fortify-rating.service')
-
-const token = 'abc123def456'
-const config = {
- public: {
- services: {
- sonar: { authorizedOrigins: ['http://sonar.petalslink.com'] },
- },
- },
- private: {
- sonarqube_token: token,
- },
-}
-
-describe('SonarFortifyRating', function() {
- cleanUpNockAfterEach()
-
- it('sends the auth information as configured', async function() {
- const scope = nock('http://sonar.petalslink.com')
- .get('/api/measures/component')
- .query({
- componentKey: 'org.ow2.petals:petals-se-ase',
- metricKeys: 'fortify-security-rating',
- })
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user: token })
- .reply(200, {
- component: {
- measures: [{ metric: 'fortify-security-rating', value: 4 }],
- },
- })
-
- expect(
- await SonarFortifyRating.invoke(
- defaultContext,
- config,
- { component: 'org.ow2.petals:petals-se-ase' },
- { server: 'http://sonar.petalslink.com' }
+describe('SonarFortifyRating', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarFortifyRating,
+ 'BasicAuth',
+ legacySonarResponse('fortify-security-rating', 4),
+ { configOverride: testAuthConfigOverride },
)
- ).to.deep.equal({
- color: 'green',
- message: '4/5',
})
-
- scope.done()
})
})
diff --git a/services/sonar/sonar-fortify-rating.tester.js b/services/sonar/sonar-fortify-rating.tester.js
index 760ba28d09f59..99b72d89114f0 100644
--- a/services/sonar/sonar-fortify-rating.tester.js
+++ b/services/sonar/sonar-fortify-rating.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// The service tests targeting the legacy SonarQube API are mocked
// because of the lack of publicly accessible, self-hosted, legacy SonarQube instances
@@ -30,7 +29,7 @@ t.create('Fortify Security Rating')
},
],
},
- })
+ }),
)
.expectBadge({
label: 'fortify-security-rating',
@@ -39,7 +38,7 @@ t.create('Fortify Security Rating')
t.create('Fortify Security Rating (legacy API supported)')
.get(
- '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -59,7 +58,7 @@ t.create('Fortify Security Rating (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'fortify-security-rating',
@@ -68,7 +67,7 @@ t.create('Fortify Security Rating (legacy API supported)')
t.create('Fortify Security Rating (legacy API not supported)')
.get(
- '/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io&sonarVersion=4.2'
+ '/michelin_kstreamplify.json?server=https://sonarcloud.io&sonarVersion=4.2',
)
.expectBadge({
label: 'fortify-security-rating',
@@ -84,7 +83,7 @@ t.create('Fortify Security Rating (nonexistent component)')
t.create('Fortify Security Rating (legacy API metric not found)')
.get(
- '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -99,7 +98,7 @@ t.create('Fortify Security Rating (legacy API metric not found)')
{
msr: [],
},
- ])
+ ]),
)
.expectBadge({
label: 'fortify-security-rating',
diff --git a/services/sonar/sonar-generic.service.js b/services/sonar/sonar-generic.service.js
index 6d1e3b94075f0..9f18e39272c0d 100644
--- a/services/sonar/sonar-generic.service.js
+++ b/services/sonar/sonar-generic.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const SonarBase = require('./sonar-base')
-const { queryParamSchema, getLabel } = require('./sonar-helpers')
+import { metric } from '../text-formatters.js'
+import SonarBase from './sonar-base.js'
+import { queryParamSchema, getLabel } from './sonar-helpers.js'
// This service is intended to be a temporary solution to avoid breaking
// any existing users/badges that were utilizing the "other" Sonar metrics
@@ -106,22 +104,16 @@ const metricNames = [
]
const metricNameRouteParam = metricNames.join('|')
-module.exports = class SonarGeneric extends SonarBase {
- static get category() {
- return 'analysis'
- }
+export default class SonarGeneric extends SonarBase {
+ static category = 'analysis'
- static get route() {
- return {
- base: 'sonar',
- pattern: `:metricName(${metricNameRouteParam})/:component`,
- queryParamSchema,
- }
+ static route = {
+ base: 'sonar',
+ pattern: `:metricName(${metricNameRouteParam})/:component/:branch*`,
+ queryParamSchema,
}
- static get defaultBadgeData() {
- return { label: 'sonar' }
- }
+ static defaultBadgeData = { label: 'sonar' }
static render({ metricName, metricValue }) {
return {
@@ -131,11 +123,12 @@ module.exports = class SonarGeneric extends SonarBase {
}
}
- async handle({ component, metricName }, { server, sonarVersion }) {
+ async handle({ component, metricName, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
metricName,
})
diff --git a/services/sonar/sonar-generic.spec.js b/services/sonar/sonar-generic.spec.js
new file mode 100644
index 0000000000000..d1cf759fef550
--- /dev/null
+++ b/services/sonar/sonar-generic.spec.js
@@ -0,0 +1,29 @@
+import { testAuth } from '../test-helpers.js'
+import SonarGeneric from './sonar-generic.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
+
+describe('SonarGeneric', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarGeneric,
+ 'BasicAuth',
+ legacySonarResponse('test', 903),
+ {
+ configOverride: testAuthConfigOverride,
+ exampleOverride: {
+ component: 'test',
+ metricName: 'test',
+ branch: 'home',
+ server:
+ testAuthConfigOverride.public.services.sonar.authorizedOrigins[0],
+ sonarVersion: '4.2',
+ },
+ },
+ )
+ })
+ })
+})
diff --git a/services/sonar/sonar-generic.tester.js b/services/sonar/sonar-generic.tester.js
index 3d5bfffd52834..d95eda3f9ef54 100644
--- a/services/sonar/sonar-generic.tester.js
+++ b/services/sonar/sonar-generic.tester.js
@@ -1,12 +1,20 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Security Rating')
+ .timeout(10000)
+ .get('/security_rating/WebExtensions.Net.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'security rating',
+ message: isMetric,
+ color: 'blue',
+ })
+
+t.create('Security Rating (branch)')
.timeout(10000)
.get(
- '/security_rating/com.luckybox:luckybox.json?server=https://sonarcloud.io'
+ '/security_rating/WebExtensions.Net/main.json?server=https://sonarcloud.io',
)
.expectBadge({
label: 'security rating',
diff --git a/services/sonar/sonar-helpers.js b/services/sonar/sonar-helpers.js
index 15c5e2373e3d8..7f93d0fe0e0c0 100644
--- a/services/sonar/sonar-helpers.js
+++ b/services/sonar/sonar-helpers.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { colorScale } = require('../color-formatters')
-const { optionalUrl } = require('../validators')
+import Joi from 'joi'
+import { queryParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import { url } from '../validators.js'
const ratingPercentageScaleSteps = [10, 20, 50, 100]
const ratingScaleColors = [
@@ -14,12 +13,12 @@ const ratingScaleColors = [
]
const negativeMetricColorScale = colorScale(
ratingPercentageScaleSteps,
- ratingScaleColors
+ ratingScaleColors,
)
const positiveMetricColorScale = colorScale(
ratingPercentageScaleSteps,
ratingScaleColors,
- true
+ true,
)
function isLegacyVersion({ sonarVersion }) {
@@ -34,41 +33,40 @@ const sonarVersionSchema = Joi.alternatives(
Joi.string()
.regex(/[0-9.]+/)
.optional(),
- Joi.number().optional()
+ Joi.number().optional(),
)
const queryParamSchema = Joi.object({
sonarVersion: sonarVersionSchema,
- server: optionalUrl.required(),
+ server: url,
}).required()
+const openApiQueryParams = queryParams(
+ { name: 'server', example: 'https://sonarcloud.io', required: true },
+ { name: 'sonarVersion', example: '4.2' },
+)
+
const queryParamWithFormatSchema = Joi.object({
sonarVersion: sonarVersionSchema,
- server: optionalUrl.required(),
- format: Joi.string()
- .allow('short', 'long')
- .optional(),
+ server: url,
+ format: Joi.equal('short', 'long').optional(),
}).required()
-const keywords = ['sonarcloud', 'sonarqube']
const documentation = `
-
- The Sonar badges will work with both SonarCloud.io and self-hosted SonarQube instances.
- Just enter the correct protocol and path for your target Sonar deployment.
-
-
- If you are targeting a legacy SonarQube instance that is version 5.3 or earlier, then be sure
- to include the version query parameter with the value of your SonarQube version.
-
{
given({ qualityState: 'OK' }).expect({
message: 'passed',
@@ -14,4 +17,15 @@ describe('SonarQualityGate', function() {
color: 'critical',
})
})
+
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarQualityGate,
+ 'BasicAuth',
+ legacySonarResponse('alert_status', 'OK'),
+ { configOverride: testAuthConfigOverride },
+ )
+ })
+ })
})
diff --git a/services/sonar/sonar-quality-gate.tester.js b/services/sonar/sonar-quality-gate.tester.js
index f7c79b1d83ae0..9f069e309e2ec 100644
--- a/services/sonar/sonar-quality-gate.tester.js
+++ b/services/sonar/sonar-quality-gate.tester.js
@@ -1,9 +1,8 @@
-'use strict'
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-
-const isQualityGateStatus = Joi.allow('passed', 'failed')
+const isQualityGateStatus = Joi.string().valid('passed', 'failed')
// The service tests targeting the legacy SonarQube API are mocked
// because of the lack of publicly accessible, self-hosted, legacy SonarQube instances
@@ -12,8 +11,15 @@ const isQualityGateStatus = Joi.allow('passed', 'failed')
// for other service tests.
t.create('Quality Gate')
+ .get('/quality_gate/michelin_kstreamplify.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'quality gate',
+ message: isQualityGateStatus,
+ })
+
+t.create('Quality Gate (branch)')
.get(
- '/quality_gate/swellaby%3Aazdo-shellcheck.json?server=https://sonarcloud.io'
+ '/quality_gate/michelin_kstreamplify/main.json?server=https://sonarcloud.io',
)
.expectBadge({
label: 'quality gate',
@@ -22,7 +28,7 @@ t.create('Quality Gate')
t.create('Quality Gate (Alert Status)')
.get(
- '/alert_status/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/alert_status/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -42,9 +48,20 @@ t.create('Quality Gate (Alert Status)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'quality gate',
message: 'passed',
})
+
+// Public instance shared by community member and permission granted for usage in tests
+// https://github.com/badges/shields/pull/6636#issuecomment-886172161
+t.create('Quality Gate (version >= 6.6)')
+ .get(
+ '/quality_gate/de.chkpnt%3Atruststorebuilder-gradle-plugin.json?server=https://sonar.chkpnt.de&sonarVersion=8.9',
+ )
+ .expectBadge({
+ label: 'quality gate',
+ message: isQualityGateStatus,
+ })
diff --git a/services/sonar/sonar-redirector.service.js b/services/sonar/sonar-redirector.service.js
index 8698721eb9e0a..f5289608c68a6 100644
--- a/services/sonar/sonar-redirector.service.js
+++ b/services/sonar/sonar-redirector.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
redirector({
name: 'SonarVersionPrefixRedirector',
category: 'analysis',
diff --git a/services/sonar/sonar-redirector.tester.js b/services/sonar/sonar-redirector.tester.js
index 7d80d667d5e2e..ade1f66c9b5fc 100644
--- a/services/sonar/sonar-redirector.tester.js
+++ b/services/sonar/sonar-redirector.tester.js
@@ -1,48 +1,46 @@
-'use strict'
+import queryString from 'querystring'
+import { ServiceTester } from '../tester.js'
-const queryString = require('querystring')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'SonarRedirect',
title: 'SonarRedirect',
pathPrefix: '/sonar',
-}))
+})
t.create('sonar version')
.get(
- '/4.2/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg'
+ '/4.2/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg',
)
.expectRedirect(
`/sonar/alert_status/org.ow2.petals:petals-se-ase.svg?${queryString.stringify(
{
server: 'http://sonar.petalslink.com',
sonarVersion: '4.2',
- }
- )}`
+ },
+ )}`,
)
t.create('sonar host parameter')
.get(
- '/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg'
+ '/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg',
)
.expectRedirect(
`/sonar/alert_status/org.ow2.petals:petals-se-ase.svg?${queryString.stringify(
{
server: 'http://sonar.petalslink.com',
- }
- )}`
+ },
+ )}`,
)
t.create('sonar host parameter with version')
.get(
- '/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg?sonarVersion=4.2'
+ '/http/sonar.petalslink.com/org.ow2.petals:petals-se-ase/alert_status.svg?sonarVersion=4.2',
)
.expectRedirect(
`/sonar/alert_status/org.ow2.petals:petals-se-ase.svg?${queryString.stringify(
{
- server: 'http://sonar.petalslink.com',
sonarVersion: '4.2',
- }
- )}`
+ server: 'http://sonar.petalslink.com',
+ },
+ )}`,
)
diff --git a/services/sonar/sonar-spec-helpers.js b/services/sonar/sonar-spec-helpers.js
new file mode 100644
index 0000000000000..1d142f4800576
--- /dev/null
+++ b/services/sonar/sonar-spec-helpers.js
@@ -0,0 +1,36 @@
+import SonarBase from './sonar-base.js'
+import { openApiQueryParams } from './sonar-helpers.js'
+
+const testAuthConfigOverride = {
+ public: {
+ services: {
+ [SonarBase.auth.serviceKey]: {
+ authorizedOrigins: [
+ openApiQueryParams.find(v => v.name === 'server').example,
+ ],
+ },
+ },
+ },
+}
+
+/**
+ * Returns a legacy sonar api response with desired key and value
+ *
+ * @param {string} key Key for the response value
+ * @param {string|number} val Value to assign to response key
+ * @returns {object} Sonar api response
+ */
+function legacySonarResponse(key, val) {
+ return [
+ {
+ msr: [
+ {
+ key,
+ val,
+ },
+ ],
+ },
+ ]
+}
+
+export { testAuthConfigOverride, legacySonarResponse }
diff --git a/services/sonar/sonar-tech-debt.service.js b/services/sonar/sonar-tech-debt.service.js
index 6a05f627d1169..315e36cc7a22a 100644
--- a/services/sonar/sonar-tech-debt.service.js
+++ b/services/sonar/sonar-tech-debt.service.js
@@ -1,52 +1,47 @@
-'use strict'
-
-const SonarBase = require('./sonar-base')
-const {
+import { pathParam } from '../index.js'
+import SonarBase from './sonar-base.js'
+import {
negativeMetricColorScale,
getLabel,
documentation,
- keywords,
queryParamSchema,
-} = require('./sonar-helpers')
+ openApiQueryParams,
+} from './sonar-helpers.js'
-module.exports = class SonarTechDebt extends SonarBase {
- static get category() {
- return 'analysis'
- }
+export default class SonarTechDebt extends SonarBase {
+ static category = 'analysis'
- static get route() {
- return {
- base: 'sonar',
- pattern: ':metric(tech_debt|sqale_debt_ratio)/:component',
- queryParamSchema,
- }
+ static route = {
+ base: 'sonar',
+ pattern: ':metric(tech_debt|sqale_debt_ratio)/:component/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Sonar Tech Debt',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- metric: 'tech_debt',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({
- debt: 1,
- metric: 'tech_debt',
- }),
- keywords,
- documentation,
+ static openApi = {
+ '/sonar/tech_debt/{component}': {
+ get: {
+ summary: 'Sonar Tech Debt',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'brave_brave-core' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
+ '/sonar/tech_debt/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Tech Debt (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'brave_brave-core' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'tech debt' }
- }
+ static defaultBadgeData = { label: 'tech debt' }
static render({ debt, metric }) {
return {
@@ -56,11 +51,12 @@ module.exports = class SonarTechDebt extends SonarBase {
}
}
- async handle({ component, metric }, { server, sonarVersion }) {
+ async handle({ component, metric, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
// Special condition for backwards compatibility.
metricName: 'sqale_debt_ratio',
})
diff --git a/services/sonar/sonar-tech-debt.spec.js b/services/sonar/sonar-tech-debt.spec.js
index 534df84a6162b..a636f8c78facb 100644
--- a/services/sonar/sonar-tech-debt.spec.js
+++ b/services/sonar/sonar-tech-debt.spec.js
@@ -1,9 +1,12 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { testAuth } from '../test-helpers.js'
+import SonarTechDebt from './sonar-tech-debt.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
-const { test, given } = require('sazerac')
-const SonarTechDebt = require('./sonar-tech-debt.service')
-
-describe('SonarTechDebt', function() {
+describe('SonarTechDebt', function () {
test(SonarTechDebt.render, () => {
given({ debt: 0 }).expect({
label: undefined,
@@ -31,4 +34,15 @@ describe('SonarTechDebt', function() {
color: 'red',
})
})
+
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarTechDebt,
+ 'BasicAuth',
+ legacySonarResponse('sqale_debt_ratio', 95),
+ { configOverride: testAuthConfigOverride },
+ )
+ })
+ })
})
diff --git a/services/sonar/sonar-tech-debt.tester.js b/services/sonar/sonar-tech-debt.tester.js
index 651bc43c8ea23..27da73743a66b 100644
--- a/services/sonar/sonar-tech-debt.tester.js
+++ b/services/sonar/sonar-tech-debt.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// The service tests targeting the legacy SonarQube API are mocked
// because of the lack of publicly accessible, self-hosted, legacy SonarQube instances
@@ -11,16 +10,23 @@ const t = (module.exports = require('../tester').createServiceTester())
t.create('Tech Debt')
.get(
- '/tech_debt/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io'
+ '/tech_debt/brave_brave-core.json?server=https://sonarcloud.io&sonarVersion=9.0',
)
.expectBadge({
label: 'tech debt',
message: isPercentage,
})
+t.create('Tech Debt (branch)')
+ .get('/tech_debt/brave_brave-core/master.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'tech debt',
+ message: isPercentage,
+ })
+
t.create('Tech Debt (legacy API supported)')
.get(
- '/tech_debt/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/tech_debt/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -40,7 +46,7 @@ t.create('Tech Debt (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'tech debt',
diff --git a/services/sonar/sonar-tests.service.js b/services/sonar/sonar-tests.service.js
index 9720f40d17a9e..12ccd0e02703a 100644
--- a/services/sonar/sonar-tests.service.js
+++ b/services/sonar/sonar-tests.service.js
@@ -1,67 +1,59 @@
-'use strict'
-
-const {
+import { pathParam } from '../index.js'
+import {
testResultQueryParamSchema,
+ testResultOpenApiQueryParams,
renderTestResultBadge,
- documentation: testResultsDocumentation,
-} = require('../test-results')
-const { metric: metricCount } = require('../text-formatters')
-const SonarBase = require('./sonar-base')
-const {
+ documentation as testResultsDocumentation,
+} from '../test-results.js'
+import { metric as metricCount } from '../text-formatters.js'
+import SonarBase from './sonar-base.js'
+import {
documentation,
- keywords,
queryParamSchema,
+ openApiQueryParams,
getLabel,
-} = require('./sonar-helpers')
+} from './sonar-helpers.js'
class SonarTestsSummary extends SonarBase {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'sonar/tests',
- pattern: ':component',
- queryParamSchema: queryParamSchema.concat(testResultQueryParamSchema),
- }
+ static category = 'test-results'
+ static route = {
+ base: 'sonar/tests',
+ pattern: ':component/:branch*',
+ queryParamSchema: queryParamSchema.concat(testResultQueryParamSchema),
}
- static get examples() {
- return [
- {
- title: 'Sonar Tests',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- compact_message: null,
- passed_label: 'passed',
- failed_label: 'failed',
- skipped_label: 'skipped',
- },
- staticPreview: this.render({
- passed: 5,
- failed: 1,
- skipped: 0,
- total: 6,
- isCompact: false,
- }),
- keywords,
- documentation: `
- ${documentation}
+ static openApi = {
+ '/sonar/tests/{component}': {
+ get: {
+ summary: 'Sonar Tests',
+ description: `${documentation}
${testResultsDocumentation}
`,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ ...openApiQueryParams,
+ ...testResultOpenApiQueryParams,
+ ],
},
- ]
+ },
+ '/sonar/tests/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Tests (branch)',
+ description: `${documentation}
+ ${testResultsDocumentation}
+ `,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ...testResultOpenApiQueryParams,
+ ],
+ },
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'tests',
- }
+ static defaultBadgeData = {
+ label: 'tests',
}
static render({
@@ -105,7 +97,7 @@ class SonarTestsSummary extends SonarBase {
}
async handle(
- { component },
+ { component, branch },
{
server,
sonarVersion,
@@ -113,12 +105,13 @@ class SonarTestsSummary extends SonarBase {
passed_label: passedLabel,
failed_label: failedLabel,
skipped_label: skippedLabel,
- }
+ },
) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
metricName: 'tests,test_failures,skipped_tests',
})
const { total, passed, failed, skipped } = this.transform({
@@ -139,81 +132,109 @@ class SonarTestsSummary extends SonarBase {
}
class SonarTests extends SonarBase {
- static get category() {
- return 'build'
- }
+ static category = 'build'
- static get route() {
- return {
- base: 'sonar',
- pattern:
- ':metric(total_tests|skipped_tests|test_failures|test_errors|test_execution_time|test_success_density)/:component',
- queryParamSchema,
- }
+ static route = {
+ base: 'sonar',
+ pattern:
+ ':metric(total_tests|skipped_tests|test_failures|test_errors|test_execution_time|test_success_density)/:component/:branch*',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Sonar Test Count',
- pattern:
- ':metric(total_tests|skipped_tests|test_failures|test_errors)/:component',
- namedParams: {
- component: 'org.ow2.petals:petals-log',
- metric: 'total_tests',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({
- metric: 'total_tests',
- value: 131,
- }),
- keywords,
- documentation,
+ static openApi = {
+ '/sonar/{metric}/{component}': {
+ get: {
+ summary: 'Sonar Test Count',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'metric',
+ example: 'total_tests',
+ schema: {
+ type: 'string',
+ enum: [
+ 'total_tests',
+ 'skipped_tests',
+ 'test_failures',
+ 'test_errors',
+ ],
+ },
+ }),
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
+ '/sonar/{metric}/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Test Count (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'metric',
+ example: 'total_tests',
+ schema: {
+ type: 'string',
+ enum: [
+ 'total_tests',
+ 'skipped_tests',
+ 'test_failures',
+ 'test_errors',
+ ],
+ },
+ }),
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Sonar Test Execution Time',
- pattern: 'test_execution_time/:component',
- namedParams: {
- component: 'swellaby:azure-pipelines-templates',
- },
- queryParams: {
- server: 'https://sonarcloud.io',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({
- metric: 'test_execution_time',
- value: 2,
- }),
- keywords,
- documentation,
+ },
+ '/sonar/test_execution_time/{component}': {
+ get: {
+ summary: 'Sonar Test Execution Time',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ ...openApiQueryParams,
+ ],
},
- {
- title: 'Sonar Test Success Rate',
- pattern: 'test_success_density/:component',
- namedParams: {
- component: 'swellaby:azure-pipelines-templates',
- },
- queryParams: {
- server: 'https://sonarcloud.io',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({
- metric: 'test_success_density',
- value: 100,
- }),
- keywords,
- documentation,
+ },
+ '/sonar/test_execution_time/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Test Execution Time (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
},
- ]
+ },
+ '/sonar/test_success_density/{component}': {
+ get: {
+ summary: 'Sonar Test Success Rate',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
+ '/sonar/test_success_density/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Test Success Rate (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({ name: 'component', example: 'michelin_kstreamplify' }),
+ pathParam({ name: 'branch', example: 'main' }),
+ ...openApiQueryParams,
+ ],
+ },
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'tests',
- }
+ static defaultBadgeData = {
+ label: 'tests',
}
static render({ value, metric }) {
@@ -236,11 +257,12 @@ class SonarTests extends SonarBase {
}
}
- async handle({ component, metric }, { server, sonarVersion }) {
+ async handle({ component, metric, branch }, { server, sonarVersion }) {
const json = await this.fetch({
sonarVersion,
server,
component,
+ branch,
// We're using 'tests' as the metric key to provide our standard
// formatted test badge (passed, failed, skipped) that exists for other
// services. Therefore, we're exposing 'total_tests' to the user, and
@@ -257,4 +279,4 @@ class SonarTests extends SonarBase {
}
}
-module.exports = [SonarTestsSummary, SonarTests]
+export { SonarTestsSummary, SonarTests }
diff --git a/services/sonar/sonar-tests.spec.js b/services/sonar/sonar-tests.spec.js
index b2043ef5c7215..9dd7681076bc1 100644
--- a/services/sonar/sonar-tests.spec.js
+++ b/services/sonar/sonar-tests.spec.js
@@ -1,9 +1,12 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { testAuth } from '../test-helpers.js'
+import { SonarTests } from './sonar-tests.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
-const { test, given } = require('sazerac')
-const SonarTests = require('./sonar-tests.service')[1]
-
-describe('SonarTests', function() {
+describe('SonarTests', function () {
test(SonarTests.render, () => {
given({ value: 0, metric: 'test_failures' }).expect({
label: 'test failures',
@@ -36,4 +39,15 @@ describe('SonarTests', function() {
color: 'red',
})
})
+
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarTests,
+ 'BasicAuth',
+ legacySonarResponse('tests', 95),
+ { configOverride: testAuthConfigOverride },
+ )
+ })
+ })
})
diff --git a/services/sonar/sonar-tests.tester.js b/services/sonar/sonar-tests.tester.js
index cc929b0f206e1..c91d6182553f3 100644
--- a/services/sonar/sonar-tests.tester.js
+++ b/services/sonar/sonar-tests.tester.js
@@ -1,24 +1,21 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const t = (module.exports = new ServiceTester({
- id: 'SonarTests',
- title: 'SonarTests',
- pathPrefix: '/sonar',
-}))
-const {
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
isDefaultTestTotals,
isDefaultCompactTestTotals,
isCustomTestTotals,
isCustomCompactTestTotals,
-} = require('../test-validators')
-const { isIntegerPercentage, isMetric } = require('../test-validators')
+ isIntegerPercentage,
+ isMetric,
+} from '../test-validators.js'
+export const t = new ServiceTester({
+ id: 'SonarTests',
+ title: 'SonarTests',
+ pathPrefix: '/sonar',
+})
const isMetricAllowZero = Joi.alternatives(
isMetric,
- Joi.number()
- .valid(0)
- .required()
+ Joi.number().valid(0).required(),
)
// The service tests targeting the legacy SonarQube API are mocked
@@ -29,9 +26,15 @@ const isMetricAllowZero = Joi.alternatives(
t.create('Tests')
.timeout(10000)
- .get(
- '/tests/swellaby:azure-pipelines-templates.json?server=https://sonarcloud.io'
- )
+ .get('/tests/michelin_kstreamplify.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'tests',
+ message: isDefaultTestTotals,
+ })
+
+t.create('Tests (branch)')
+ .timeout(10000)
+ .get('/tests/michelin_kstreamplify/main.json?server=https://sonarcloud.io')
.expectBadge({
label: 'tests',
message: isDefaultTestTotals,
@@ -39,7 +42,7 @@ t.create('Tests')
t.create('Tests (legacy API supported)')
.get(
- '/tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -67,7 +70,7 @@ t.create('Tests (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'tests',
@@ -76,7 +79,7 @@ t.create('Tests (legacy API supported)')
t.create('Tests with compact message')
.timeout(10000)
- .get('/tests/swellaby:azure-pipelines-templates.json', {
+ .get('/tests/michelin_kstreamplify.json', {
qs: {
compact_message: null,
server: 'https://sonarcloud.io',
@@ -86,7 +89,7 @@ t.create('Tests with compact message')
t.create('Tests with custom labels')
.timeout(10000)
- .get('/tests/swellaby:azure-pipelines-templates.json', {
+ .get('/tests/michelin_kstreamplify.json', {
qs: {
server: 'https://sonarcloud.io',
passed_label: 'good',
@@ -98,7 +101,7 @@ t.create('Tests with custom labels')
t.create('Tests with compact message and custom labels')
.timeout(10000)
- .get('/tests/swellaby:azure-pipelines-templates.json', {
+ .get('/tests/michelin_kstreamplify.json', {
qs: {
server: 'https://sonarcloud.io',
compact_message: null,
@@ -113,9 +116,17 @@ t.create('Tests with compact message and custom labels')
})
t.create('Total Test Count')
+ .timeout(10000)
+ .get('/total_tests/michelin_kstreamplify.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'total tests',
+ message: isMetric,
+ })
+
+t.create('Total Test Count (branch)')
.timeout(10000)
.get(
- '/total_tests/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io'
+ '/total_tests/michelin_kstreamplify/main.json?server=https://sonarcloud.io',
)
.expectBadge({
label: 'total tests',
@@ -124,7 +135,7 @@ t.create('Total Test Count')
t.create('Total Test Count (legacy API supported)')
.get(
- '/total_tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/total_tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -144,7 +155,7 @@ t.create('Total Test Count (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'total tests',
@@ -153,9 +164,7 @@ t.create('Total Test Count (legacy API supported)')
t.create('Test Failures Count')
.timeout(10000)
- .get(
- '/test_failures/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io'
- )
+ .get('/test_failures/michelin_kstreamplify.json?server=https://sonarcloud.io')
.expectBadge({
label: 'test failures',
message: isMetricAllowZero,
@@ -163,7 +172,7 @@ t.create('Test Failures Count')
t.create('Test Failures Count (legacy API supported)')
.get(
- '/test_failures/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/test_failures/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -183,7 +192,7 @@ t.create('Test Failures Count (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'test failures',
@@ -192,9 +201,7 @@ t.create('Test Failures Count (legacy API supported)')
t.create('Test Errors Count')
.timeout(10000)
- .get(
- '/test_errors/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io'
- )
+ .get('/test_errors/michelin_kstreamplify.json?server=https://sonarcloud.io')
.expectBadge({
label: 'test errors',
message: isMetricAllowZero,
@@ -202,7 +209,7 @@ t.create('Test Errors Count')
t.create('Test Errors Count (legacy API supported)')
.get(
- '/test_errors/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/test_errors/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -222,7 +229,7 @@ t.create('Test Errors Count (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'test errors',
@@ -231,9 +238,7 @@ t.create('Test Errors Count (legacy API supported)')
t.create('Skipped Tests Count')
.timeout(10000)
- .get(
- '/skipped_tests/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io'
- )
+ .get('/skipped_tests/michelin_kstreamplify.json?server=https://sonarcloud.io')
.expectBadge({
label: 'skipped tests',
message: isMetricAllowZero,
@@ -241,7 +246,7 @@ t.create('Skipped Tests Count')
t.create('Skipped Tests Count (legacy API supported)')
.get(
- '/skipped_tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/skipped_tests/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -261,7 +266,7 @@ t.create('Skipped Tests Count (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'skipped tests',
@@ -271,7 +276,7 @@ t.create('Skipped Tests Count (legacy API supported)')
t.create('Test Success Rate')
.timeout(10000)
.get(
- '/test_success_density/swellaby:azdo-shellcheck.json?server=https://sonarcloud.io'
+ '/test_success_density/michelin_kstreamplify.json?server=https://sonarcloud.io',
)
.expectBadge({
label: 'tests',
@@ -280,7 +285,7 @@ t.create('Test Success Rate')
t.create('Test Success Rate (legacy API supported)')
.get(
- '/test_success_density/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/test_success_density/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -300,7 +305,7 @@ t.create('Test Success Rate (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'tests',
diff --git a/services/sonar/sonar-violations.service.js b/services/sonar/sonar-violations.service.js
index caa4f7516f6e0..10291fcd8bdbe 100644
--- a/services/sonar/sonar-violations.service.js
+++ b/services/sonar/sonar-violations.service.js
@@ -1,18 +1,17 @@
-'use strict'
-
-const { colorScale } = require('../color-formatters')
-const { metric } = require('../text-formatters')
-const SonarBase = require('./sonar-base')
-const {
+import { pathParam, queryParam } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import { metric } from '../text-formatters.js'
+import SonarBase from './sonar-base.js'
+import {
getLabel,
documentation,
- keywords,
queryParamWithFormatSchema,
-} = require('./sonar-helpers')
+ openApiQueryParams,
+} from './sonar-helpers.js'
const violationsColorScale = colorScale(
[1, 2, 3, 5],
- ['brightgreen', 'yellowgreen', 'yellow', 'orange', 'red']
+ ['brightgreen', 'yellowgreen', 'yellow', 'orange', 'red'],
)
const violationCategoryColorMap = {
@@ -23,68 +22,69 @@ const violationCategoryColorMap = {
info_violations: 'green',
}
-module.exports = class SonarViolations extends SonarBase {
- static get category() {
- return 'analysis'
- }
+export default class SonarViolations extends SonarBase {
+ static category = 'analysis'
- static get route() {
- return {
- base: 'sonar',
- pattern:
- ':metric(violations|blocker_violations|critical_violations|major_violations|minor_violations|info_violations)/:component',
- queryParamSchema: queryParamWithFormatSchema,
- }
+ static route = {
+ base: 'sonar',
+ pattern:
+ ':metric(violations|blocker_violations|critical_violations|major_violations|minor_violations|info_violations)/:component/:branch*',
+ queryParamSchema: queryParamWithFormatSchema,
}
- static get examples() {
- return [
- {
- title: 'Sonar Violations (short format)',
- namedParams: {
- component: 'swellaby:azdo-shellcheck',
- metric: 'violations',
- },
- queryParams: {
- server: 'https://sonarcloud.io',
- format: 'short',
- sonarVersion: '4.2',
- },
- staticPreview: this.render({
- violations: 0,
- metricName: 'violations',
- format: 'short',
- }),
- keywords,
- documentation,
+ static openApi = {
+ '/sonar/{metric}/{component}': {
+ get: {
+ summary: 'Sonar Violations',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'metric',
+ example: 'violations',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ }),
+ pathParam({ name: 'component', example: 'brave_brave-core' }),
+ ...openApiQueryParams,
+ queryParam({
+ name: 'format',
+ example: 'long',
+ schema: {
+ type: 'string',
+ enum: ['short', 'long'],
+ },
+ description: 'If not specified, the default is `short`.',
+ }),
+ ],
},
- {
- title: 'Sonar Violations (long format)',
- namedParams: {
- component: 'org.ow2.petals:petals-se-ase',
- metric: 'violations',
- },
- queryParams: {
- server: 'http://sonar.petalslink.com',
- format: 'long',
- },
- staticPreview: this.render({
- violations: {
- info_violations: 2,
- minor_violations: 1,
- },
- metricName: 'violations',
- format: 'long',
- }),
- keywords,
- documentation,
+ },
+ '/sonar/{metric}/{component}/{branch}': {
+ get: {
+ summary: 'Sonar Violations (branch)',
+ description: documentation,
+ parameters: [
+ pathParam({
+ name: 'metric',
+ example: 'violations',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ }),
+ pathParam({ name: 'component', example: ':brave_brave-core' }),
+ pathParam({ name: 'branch', example: 'master' }),
+ ...openApiQueryParams,
+ queryParam({
+ name: 'format',
+ example: 'long',
+ schema: {
+ type: 'string',
+ enum: ['short', 'long'],
+ },
+ description: 'If not specified, the default is `short`.',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'violations' }
- }
+ static defaultBadgeData = { label: 'violations' }
static renderLongViolationsBadge(violations) {
if (violations.violations === 0) {
@@ -154,7 +154,10 @@ module.exports = class SonarViolations extends SonarBase {
return { violations: metrics }
}
- async handle({ component, metric }, { server, sonarVersion, format }) {
+ async handle(
+ { component, metric, branch },
+ { server, sonarVersion, format },
+ ) {
// If the user has requested the long format for the violations badge
// then we need to include each individual violation metric in the call to the API
// in order to get the count breakdown per each violation category.
@@ -166,6 +169,7 @@ module.exports = class SonarViolations extends SonarBase {
sonarVersion,
server,
component,
+ branch,
metricName: metricKeys,
})
diff --git a/services/sonar/sonar-violations.spec.js b/services/sonar/sonar-violations.spec.js
index 6983c6da97e2f..2c24087279bc2 100644
--- a/services/sonar/sonar-violations.spec.js
+++ b/services/sonar/sonar-violations.spec.js
@@ -1,10 +1,13 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { metric } from '../text-formatters.js'
+import { testAuth } from '../test-helpers.js'
+import SonarViolations from './sonar-violations.service.js'
+import {
+ legacySonarResponse,
+ testAuthConfigOverride,
+} from './sonar-spec-helpers.js'
-const { test, given } = require('sazerac')
-const { metric } = require('../text-formatters')
-const SonarViolations = require('./sonar-violations.service')
-
-describe('SonarViolations', function() {
+describe('SonarViolations', function () {
test(SonarViolations.render, () => {
given({ metricName: 'violations', violations: 1003 }).expect({
message: metric(1003),
@@ -112,4 +115,18 @@ describe('SonarViolations', function() {
color: 'red',
})
})
+
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SonarViolations,
+ 'BasicAuth',
+ legacySonarResponse('violations', 95),
+ {
+ configOverride: testAuthConfigOverride,
+ exampleOverride: { format: 'short' },
+ },
+ )
+ })
+ })
})
diff --git a/services/sonar/sonar-violations.tester.js b/services/sonar/sonar-violations.tester.js
index dc3d7e9e95eda..74bbc77b3cc2e 100644
--- a/services/sonar/sonar-violations.tester.js
+++ b/services/sonar/sonar-violations.tester.js
@@ -1,13 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isMetric, withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isMetric, withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isViolationsLongFormMetric = Joi.alternatives(
- Joi.allow(0),
+ Joi.equal(0),
withRegex(
- /(([\d]+) (blocker|critical|major|minor|info))(,\s([\d]+) (critical|major|minor|info))?/
- )
+ /(([\d]+) (blocker|critical|major|minor|info))(,\s([\d]+) (critical|major|minor|info))?/,
+ ),
)
// The service tests targeting the legacy SonarQube API are mocked
@@ -18,9 +17,15 @@ const isViolationsLongFormMetric = Joi.alternatives(
t.create('Violations')
.timeout(10000)
- .get(
- '/violations/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io'
- )
+ .get('/violations/brave_brave-core.json?server=https://sonarcloud.io')
+ .expectBadge({
+ label: 'violations',
+ message: isMetric,
+ })
+
+t.create('Violations (branch)')
+ .timeout(10000)
+ .get('/violations/brave_brave-core/master.json?server=https://sonarcloud.io')
.expectBadge({
label: 'violations',
message: isMetric,
@@ -28,7 +33,7 @@ t.create('Violations')
t.create('Violations (legacy API supported)')
.get(
- '/violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -48,7 +53,7 @@ t.create('Violations (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'violations',
@@ -58,7 +63,7 @@ t.create('Violations (legacy API supported)')
t.create('Violations Long Format')
.timeout(10000)
.get(
- '/violations/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io&format=long'
+ '/violations/brave_brave-core.json?server=https://sonarcloud.io&format=long',
)
.expectBadge({
label: 'violations',
@@ -67,7 +72,7 @@ t.create('Violations Long Format')
t.create('Violations Long Format (legacy API supported)')
.get(
- '/violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2&format=long'
+ '/violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2&format=long',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -108,7 +113,7 @@ t.create('Violations Long Format (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'violations',
@@ -117,9 +122,7 @@ t.create('Violations Long Format (legacy API supported)')
t.create('Blocker Violations')
.timeout(10000)
- .get(
- '/blocker_violations/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io'
- )
+ .get('/blocker_violations/apache_camel.json?server=https://sonarcloud.io')
.expectBadge({
label: 'blocker violations',
message: isMetric,
@@ -127,7 +130,7 @@ t.create('Blocker Violations')
t.create('Blocker Violations (legacy API supported)')
.get(
- '/blocker_violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/blocker_violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -147,7 +150,7 @@ t.create('Blocker Violations (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'blocker violations',
@@ -157,7 +160,7 @@ t.create('Blocker Violations (legacy API supported)')
t.create('Critical Violations')
.timeout(10000)
.get(
- '/critical_violations/org.sonarsource.sonarqube%3Asonarqube.json?server=https://sonarcloud.io'
+ '/critical_violations/brave_brave-core.json?server=https://sonarcloud.io',
)
.expectBadge({
label: 'critical violations',
@@ -166,7 +169,7 @@ t.create('Critical Violations')
t.create('Critical Violations (legacy API supported)')
.get(
- '/critical_violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2'
+ '/critical_violations/org.ow2.petals%3Apetals-se-ase.json?server=http://sonar.petalslink.com&sonarVersion=4.2',
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
@@ -186,7 +189,7 @@ t.create('Critical Violations (legacy API supported)')
},
],
},
- ])
+ ]),
)
.expectBadge({
label: 'critical violations',
diff --git a/services/sourceforge/sourceforge-base.js b/services/sourceforge/sourceforge-base.js
new file mode 100644
index 0000000000000..e1c973a8cf16e
--- /dev/null
+++ b/services/sourceforge/sourceforge-base.js
@@ -0,0 +1,13 @@
+import { BaseJsonService } from '../index.js'
+
+export default class BaseSourceForgeService extends BaseJsonService {
+ async fetch({ project, schema }) {
+ return this._requestJson({
+ url: `https://sourceforge.net/rest/p/${project}/`,
+ schema,
+ httpErrors: {
+ 404: 'project not found',
+ },
+ })
+ }
+}
diff --git a/services/sourceforge/sourceforge-commit-count-redirect.service.js b/services/sourceforge/sourceforge-commit-count-redirect.service.js
new file mode 100644
index 0000000000000..1efcd416bd56d
--- /dev/null
+++ b/services/sourceforge/sourceforge-commit-count-redirect.service.js
@@ -0,0 +1,15 @@
+import { redirector } from '../index.js'
+
+export default redirector({
+ // SourceForge commit count service used to only have project name as a parameter
+ // and the repository name was always `git`.
+ // The service was later updated to have the repository name as a parameter.
+ // This redirector is used to keep the old URLs working.
+ category: 'activity',
+ route: {
+ base: 'sourceforge/commit-count',
+ pattern: ':project',
+ },
+ transformPath: ({ project }) => `/sourceforge/commit-count/${project}/git`,
+ dateAdded: new Date('2025-03-15'),
+})
diff --git a/services/sourceforge/sourceforge-commit-count-redirect.tester.js b/services/sourceforge/sourceforge-commit-count-redirect.tester.js
new file mode 100644
index 0000000000000..3e09678b72f33
--- /dev/null
+++ b/services/sourceforge/sourceforge-commit-count-redirect.tester.js
@@ -0,0 +1,6 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('commit count (redirect)')
+ .get('/guitarix.json')
+ .expectRedirect('/sourceforge/commit-count/guitarix/git.json')
diff --git a/services/sourceforge/sourceforge-commit-count.service.js b/services/sourceforge/sourceforge-commit-count.service.js
new file mode 100644
index 0000000000000..0a8d2bf916189
--- /dev/null
+++ b/services/sourceforge/sourceforge-commit-count.service.js
@@ -0,0 +1,62 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+
+const schema = Joi.object({
+ commit_count: Joi.number().required(),
+}).required()
+
+export default class SourceforgeCommitCount extends BaseJsonService {
+ static category = 'activity'
+
+ static route = {
+ base: 'sourceforge/commit-count',
+ pattern: ':project/:repo',
+ }
+
+ static openApi = {
+ '/sourceforge/commit-count/{project}/{repo}': {
+ get: {
+ summary: 'SourceForge Commit Count',
+ parameters: pathParams(
+ {
+ name: 'project',
+ example: 'guitarix',
+ },
+ {
+ name: 'repo',
+ example: 'git',
+ description:
+ 'The repository name, usually `git` but might be different.',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'commit count' }
+
+ static render({ commitCount }) {
+ return {
+ message: metric(commitCount),
+ color: 'blue',
+ }
+ }
+
+ async fetch({ project, repo }) {
+ return this._requestJson({
+ url: `https://sourceforge.net/rest/p/${project}/${repo}`,
+ schema,
+ httpErrors: {
+ 404: 'project or repo not found',
+ },
+ })
+ }
+
+ async handle({ project, repo }) {
+ const body = await this.fetch({ project, repo })
+ return this.constructor.render({
+ commitCount: body.commit_count,
+ })
+ }
+}
diff --git a/services/sourceforge/sourceforge-commit-count.tester.js b/services/sourceforge/sourceforge-commit-count.tester.js
new file mode 100644
index 0000000000000..24ad6e7db0326
--- /dev/null
+++ b/services/sourceforge/sourceforge-commit-count.tester.js
@@ -0,0 +1,19 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('commit count')
+ .get('/guitarix/git.json')
+ .expectBadge({ label: 'commit count', message: isMetric })
+
+t.create('commit count (non default repo)')
+ .get('/opencamera/code.json')
+ .expectBadge({ label: 'commit count', message: isMetric })
+
+t.create('commit count (project not found)')
+ .get('/that-doesnt-exist/git.json')
+ .expectBadge({ label: 'commit count', message: 'project or repo not found' })
+
+t.create('commit count (repo not found)')
+ .get('/guitarix/invalid-repo.json')
+ .expectBadge({ label: 'commit count', message: 'project or repo not found' })
diff --git a/services/sourceforge/sourceforge-contributors.service.js b/services/sourceforge/sourceforge-contributors.service.js
new file mode 100644
index 0000000000000..8d08dd8c35bad
--- /dev/null
+++ b/services/sourceforge/sourceforge-contributors.service.js
@@ -0,0 +1,44 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderContributorBadge } from '../contributor-count.js'
+import BaseSourceForgeService from './sourceforge-base.js'
+
+const schema = Joi.object({
+ developers: Joi.array().required(),
+}).required()
+
+export default class SourceforgeContributors extends BaseSourceForgeService {
+ static category = 'activity'
+
+ static route = {
+ base: 'sourceforge/contributors',
+ pattern: ':project',
+ }
+
+ static openApi = {
+ '/sourceforge/contributors/{project}': {
+ get: {
+ summary: 'SourceForge Contributors',
+ parameters: pathParams({
+ name: 'project',
+ example: 'guitarix',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'contributors' }
+
+ static render({ contributorCount }) {
+ return renderContributorBadge({
+ contributorCount,
+ })
+ }
+
+ async handle({ project }) {
+ const body = await this.fetch({ project, schema })
+ return this.constructor.render({
+ contributorCount: body.developers.length,
+ })
+ }
+}
diff --git a/services/sourceforge/sourceforge-contributors.tester.js b/services/sourceforge/sourceforge-contributors.tester.js
new file mode 100644
index 0000000000000..b0fa07216963d
--- /dev/null
+++ b/services/sourceforge/sourceforge-contributors.tester.js
@@ -0,0 +1,11 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('contributors')
+ .get('/guitarix.json')
+ .expectBadge({ label: 'contributors', message: isMetric })
+
+t.create('contributors (project not found)')
+ .get('/that-doesnt-exist.json')
+ .expectBadge({ label: 'contributors', message: 'project not found' })
diff --git a/services/sourceforge/sourceforge-downloads.service.js b/services/sourceforge/sourceforge-downloads.service.js
new file mode 100644
index 0000000000000..269e5952c82ec
--- /dev/null
+++ b/services/sourceforge/sourceforge-downloads.service.js
@@ -0,0 +1,110 @@
+import Joi from 'joi'
+import dayjs from 'dayjs'
+import { renderDownloadsBadge } from '../downloads.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+
+const schema = Joi.object({
+ total: nonNegativeInteger,
+}).required()
+
+const intervalMap = {
+ dd: {
+ startDate: endDate => endDate,
+ interval: 'day',
+ },
+ dw: {
+ // 6 days, since date range is inclusive,
+ startDate: endDate => dayjs(endDate).subtract(6, 'days'),
+ interval: 'week',
+ },
+ dm: {
+ startDate: endDate => dayjs(endDate).subtract(30, 'days'),
+ interval: 'month',
+ },
+ dt: {
+ startDate: () => dayjs(0),
+ },
+}
+
+export default class SourceforgeDownloads extends BaseJsonService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'sourceforge',
+ pattern: ':interval(dd|dw|dm|dt)/:project/:folder*',
+ }
+
+ static openApi = {
+ '/sourceforge/{interval}/{project}': {
+ get: {
+ summary: 'SourceForge Downloads',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ description: 'Daily, Weekly, Monthly, or Total downloads',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ },
+ { name: 'project', example: 'sevenzip' },
+ ),
+ },
+ },
+ '/sourceforge/{interval}/{project}/{folder}': {
+ get: {
+ summary: 'SourceForge Downloads (folder)',
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ description: 'Daily, Weekly, Monthly, or Total downloads',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ },
+ { name: 'project', example: 'arianne' },
+ { name: 'folder', example: 'stendhal' },
+ ),
+ },
+ },
+ }
+
+ static _cacheLength = 2400
+
+ static defaultBadgeData = { label: 'sourceforge' }
+
+ static render({ downloads, interval }) {
+ return renderDownloadsBadge({
+ downloads,
+ labelOverride: 'downloads',
+ interval: intervalMap[interval].interval,
+ })
+ }
+
+ async fetch({ interval, project, folder }) {
+ const url = `https://sourceforge.net/projects/${project}/files/${
+ folder ? `${folder}/` : ''
+ }stats/json`
+ // get yesterday since today is incomplete
+ const endDate = dayjs().subtract(24, 'hours')
+ const startDate = intervalMap[interval].startDate(endDate)
+ const options = {
+ searchParams: {
+ start_date: startDate.format('YYYY-MM-DD'),
+ end_date: endDate.format('YYYY-MM-DD'),
+ },
+ }
+
+ return this._requestJson({
+ schema,
+ url,
+ options,
+ httpErrors: {
+ 404: 'project not found',
+ },
+ })
+ }
+
+ async handle({ interval, project, folder }) {
+ const { total: downloads } = await this.fetch({ interval, project, folder })
+ return this.constructor.render({ interval, downloads })
+ }
+}
diff --git a/services/sourceforge/sourceforge-downloads.tester.js b/services/sourceforge/sourceforge-downloads.tester.js
new file mode 100644
index 0000000000000..e74de6f6fbf4d
--- /dev/null
+++ b/services/sourceforge/sourceforge-downloads.tester.js
@@ -0,0 +1,40 @@
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('total downloads').get('/dt/sevenzip.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('total downloads (with subdirs)')
+ .get('/dt/smartmontools/smartmontools/7.1.json')
+ .expectBadge({
+ label: 'downloads',
+ message: isMetric,
+ })
+
+t.create('monthly downloads').get('/dm/sevenzip.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('weekly downloads').get('/dw/sevenzip.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('daily downloads').get('/dd/sevenzip.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('downloads folder').get('/dm/arianne/stendhal.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
+
+t.create('invalid project').get('/dd/invalid.json').expectBadge({
+ label: 'sourceforge',
+ message: 'project not found',
+})
diff --git a/services/sourceforge/sourceforge-languages.service.js b/services/sourceforge/sourceforge-languages.service.js
new file mode 100644
index 0000000000000..d8deee1a57632
--- /dev/null
+++ b/services/sourceforge/sourceforge-languages.service.js
@@ -0,0 +1,45 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import BaseSourceForgeService from './sourceforge-base.js'
+
+const schema = Joi.object({
+ categories: Joi.object({
+ language: Joi.array().required(),
+ }).required(),
+}).required()
+
+export default class SourceforgeLanguages extends BaseSourceForgeService {
+ static category = 'analysis'
+
+ static route = {
+ base: 'sourceforge/languages',
+ pattern: ':project',
+ }
+
+ static openApi = {
+ '/sourceforge/languages/{project}': {
+ get: {
+ summary: 'SourceForge Languages',
+ parameters: pathParams({
+ name: 'project',
+ example: 'mingw',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'languages' }
+
+ static render(languages) {
+ return {
+ message: metric(languages),
+ color: 'blue',
+ }
+ }
+
+ async handle({ project }) {
+ const body = await this.fetch({ project, schema })
+ return this.constructor.render(body.categories.language.length)
+ }
+}
diff --git a/services/sourceforge/sourceforge-languages.tester.js b/services/sourceforge/sourceforge-languages.tester.js
new file mode 100644
index 0000000000000..f79186e205e22
--- /dev/null
+++ b/services/sourceforge/sourceforge-languages.tester.js
@@ -0,0 +1,11 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('languages')
+ .get('/guitarix.json')
+ .expectBadge({ label: 'languages', message: isMetric })
+
+t.create('languages (project not found)')
+ .get('/that-doesnt-exist.json')
+ .expectBadge({ label: 'languages', message: 'project not found' })
diff --git a/services/sourceforge/sourceforge-last-commit-redirect.service.js b/services/sourceforge/sourceforge-last-commit-redirect.service.js
new file mode 100644
index 0000000000000..b08627250efb9
--- /dev/null
+++ b/services/sourceforge/sourceforge-last-commit-redirect.service.js
@@ -0,0 +1,15 @@
+import { redirector } from '../index.js'
+
+export default redirector({
+ // SourceForge last commit service used to only have project name as a parameter
+ // and the repository name was always `git`.
+ // The service was later updated to have the repository name as a parameter.
+ // This redirector is used to keep the old URLs working.
+ category: 'activity',
+ route: {
+ base: 'sourceforge/last-commit',
+ pattern: ':project',
+ },
+ transformPath: ({ project }) => `/sourceforge/last-commit/${project}/git`,
+ dateAdded: new Date('2025-03-08'),
+})
diff --git a/services/sourceforge/sourceforge-last-commit-redirect.tester.js b/services/sourceforge/sourceforge-last-commit-redirect.tester.js
new file mode 100644
index 0000000000000..b8d5b4f66c267
--- /dev/null
+++ b/services/sourceforge/sourceforge-last-commit-redirect.tester.js
@@ -0,0 +1,6 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('last commit (redirect)')
+ .get('/guitarix.json')
+ .expectRedirect('/sourceforge/last-commit/guitarix/git.json')
diff --git a/services/sourceforge/sourceforge-last-commit.service.js b/services/sourceforge/sourceforge-last-commit.service.js
new file mode 100644
index 0000000000000..fd7dc766c38b8
--- /dev/null
+++ b/services/sourceforge/sourceforge-last-commit.service.js
@@ -0,0 +1,59 @@
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+
+const schema = Joi.object({
+ commits: Joi.array()
+ .items(
+ Joi.object({
+ committed_date: Joi.string().required(),
+ }).required(),
+ )
+ .required(),
+}).required()
+
+export default class SourceforgeLastCommit extends BaseJsonService {
+ static category = 'activity'
+
+ static route = {
+ base: 'sourceforge/last-commit',
+ pattern: ':project/:repo',
+ }
+
+ static openApi = {
+ '/sourceforge/last-commit/{project}/{repo}': {
+ get: {
+ summary: 'SourceForge Last Commit',
+ parameters: pathParams(
+ {
+ name: 'project',
+ example: 'guitarix',
+ },
+ {
+ name: 'repo',
+ example: 'git',
+ description:
+ 'The repository name, usually `git` but might be different.',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'last commit' }
+
+ async fetch({ project, repo }) {
+ return this._requestJson({
+ url: `https://sourceforge.net/rest/p/${project}/${repo}/commits`,
+ schema,
+ httpErrors: {
+ 404: 'project or repo not found',
+ },
+ })
+ }
+
+ async handle({ project, repo }) {
+ const body = await this.fetch({ project, repo })
+ return renderDateBadge(body.commits[0].committed_date)
+ }
+}
diff --git a/services/sourceforge/sourceforge-last-commit.tester.js b/services/sourceforge/sourceforge-last-commit.tester.js
new file mode 100644
index 0000000000000..94bee370396fc
--- /dev/null
+++ b/services/sourceforge/sourceforge-last-commit.tester.js
@@ -0,0 +1,19 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('last commit')
+ .get('/guitarix/git.json')
+ .expectBadge({ label: 'last commit', message: isFormattedDate })
+
+t.create('last commit (non default repo)')
+ .get('/opencamera/code.json')
+ .expectBadge({ label: 'last commit', message: isFormattedDate })
+
+t.create('last commit (project not found)')
+ .get('/that-doesnt-exist/fake.json')
+ .expectBadge({ label: 'last commit', message: 'project or repo not found' })
+
+t.create('last commit (repo not found)')
+ .get('/guitarix/fake-repo.json')
+ .expectBadge({ label: 'last commit', message: 'project or repo not found' })
diff --git a/services/sourceforge/sourceforge-open-tickets.service.js b/services/sourceforge/sourceforge-open-tickets.service.js
index 971ed71d8ef08..7c1cf76ae31a5 100644
--- a/services/sourceforge/sourceforge-open-tickets.service.js
+++ b/services/sourceforge/sourceforge-open-tickets.service.js
@@ -1,44 +1,42 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
count: nonNegativeInteger.required(),
}).required()
-module.exports = class SourceforgeOpenTickets extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class SourceforgeOpenTickets extends BaseJsonService {
+ static category = 'other'
- static get route() {
- return {
- base: 'sourceforge/open-tickets',
- pattern: ':project/:type(bugs|feature-requests)',
- }
+ static route = {
+ base: 'sourceforge/open-tickets',
+ pattern: ':project/:type(bugs|feature-requests)',
}
- static get examples() {
- return [
- {
- title: 'Sourceforge Open Tickets',
- namedParams: {
- type: 'bugs',
- project: 'sevenzip',
- },
- staticPreview: this.render({ count: 1338 }),
+ static openApi = {
+ '/sourceforge/open-tickets/{project}/{type}': {
+ get: {
+ summary: 'Sourceforge Open Tickets',
+ parameters: pathParams(
+ {
+ name: 'project',
+ example: 'sevenzip',
+ },
+ {
+ name: 'type',
+ example: 'bugs',
+ schema: { type: 'string', enum: this.getEnum('type') },
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'open tickets',
- color: 'blue',
- }
+ static defaultBadgeData = {
+ label: 'open tickets',
+ color: 'blue',
}
static render({ count }) {
@@ -53,7 +51,7 @@ module.exports = class SourceforgeOpenTickets extends BaseJsonService {
return this._requestJson({
schema,
url,
- errorMessages: {
+ httpErrors: {
404: 'project not found',
},
})
diff --git a/services/sourceforge/sourceforge-open-tickets.tester.js b/services/sourceforge/sourceforge-open-tickets.tester.js
index 3636eb9aeaa47..4fa123e87b353 100644
--- a/services/sourceforge/sourceforge-open-tickets.tester.js
+++ b/services/sourceforge/sourceforge-open-tickets.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('bugs')
.get('/sevenzip/bugs.json')
@@ -19,9 +18,7 @@ t.create('feature requests')
})
.timeout(10000)
-t.create('invalid project')
- .get('/invalid/bugs.json')
- .expectBadge({
- label: 'open tickets',
- message: 'project not found',
- })
+t.create('invalid project').get('/invalid/bugs.json').expectBadge({
+ label: 'open tickets',
+ message: 'project not found',
+})
diff --git a/services/sourceforge/sourceforge-platform.service.js b/services/sourceforge/sourceforge-platform.service.js
new file mode 100644
index 0000000000000..27bf4f06db30b
--- /dev/null
+++ b/services/sourceforge/sourceforge-platform.service.js
@@ -0,0 +1,49 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import BaseSourceForgeService from './sourceforge-base.js'
+
+const schema = Joi.object({
+ categories: Joi.object({
+ os: Joi.array()
+ .items({
+ fullname: Joi.string().required(),
+ })
+ .required(),
+ }).required(),
+}).required()
+
+export default class SourceforgePlatform extends BaseSourceForgeService {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'sourceforge/platform',
+ pattern: ':project',
+ }
+
+ static openApi = {
+ '/sourceforge/platform/{project}': {
+ get: {
+ summary: 'SourceForge Platform',
+ parameters: pathParams({
+ name: 'project',
+ example: 'guitarix',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'platform' }
+
+ static render({ platforms }) {
+ return {
+ message: platforms.join(' | '),
+ }
+ }
+
+ async handle({ project }) {
+ const body = await this.fetch({ project, schema })
+ return this.constructor.render({
+ platforms: body.categories.os.map(obj => obj.fullname),
+ })
+ }
+}
diff --git a/services/sourceforge/sourceforge-platform.tester.js b/services/sourceforge/sourceforge-platform.tester.js
new file mode 100644
index 0000000000000..836fa86754433
--- /dev/null
+++ b/services/sourceforge/sourceforge-platform.tester.js
@@ -0,0 +1,11 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('platform')
+ .get('/guitarix.json')
+ .expectBadge({ label: 'platform', message: Joi.string().required() })
+
+t.create('platform (project not found)')
+ .get('/that-doesnt-exist.json')
+ .expectBadge({ label: 'platform', message: 'project not found' })
diff --git a/services/sourceforge/sourceforge-translations.service.js b/services/sourceforge/sourceforge-translations.service.js
new file mode 100644
index 0000000000000..9f0edfc700e55
--- /dev/null
+++ b/services/sourceforge/sourceforge-translations.service.js
@@ -0,0 +1,47 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import BaseSourceForgeService from './sourceforge-base.js'
+
+const schema = Joi.object({
+ categories: Joi.object({
+ translation: Joi.array().required(),
+ }).required(),
+}).required()
+
+export default class SourceforgeTranslations extends BaseSourceForgeService {
+ static category = 'activity'
+
+ static route = {
+ base: 'sourceforge/translations',
+ pattern: ':project',
+ }
+
+ static openApi = {
+ '/sourceforge/translations/{project}': {
+ get: {
+ summary: 'SourceForge Translations',
+ parameters: pathParams({
+ name: 'project',
+ example: 'guitarix',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'translations' }
+
+ static render({ translationCount }) {
+ return {
+ message: metric(translationCount),
+ color: 'blue',
+ }
+ }
+
+ async handle({ project }) {
+ const body = await this.fetch({ project, schema })
+ return this.constructor.render({
+ translationCount: body.categories.translation.length,
+ })
+ }
+}
diff --git a/services/sourceforge/sourceforge-translations.tester.js b/services/sourceforge/sourceforge-translations.tester.js
new file mode 100644
index 0000000000000..b66947190d93a
--- /dev/null
+++ b/services/sourceforge/sourceforge-translations.tester.js
@@ -0,0 +1,11 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('translations')
+ .get('/guitarix.json')
+ .expectBadge({ label: 'translations', message: isMetric })
+
+t.create('translations (project not found)')
+ .get('/that-doesnt-exist.json')
+ .expectBadge({ label: 'translations', message: 'project not found' })
diff --git a/services/sourceforge/sourceforge.service.js b/services/sourceforge/sourceforge.service.js
deleted file mode 100644
index 4eebed4b1af8b..0000000000000
--- a/services/sourceforge/sourceforge.service.js
+++ /dev/null
@@ -1,116 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const moment = require('moment')
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- total: nonNegativeInteger,
-}).required()
-
-const intervalMap = {
- dd: {
- startDate: endDate => endDate,
- suffix: '/day',
- },
- dw: {
- // 6 days, since date range is inclusive,
- startDate: endDate => moment(endDate).subtract(6, 'days'),
- suffix: '/week',
- },
- dm: {
- startDate: endDate => moment(endDate).subtract(30, 'days'),
- suffix: '/month',
- },
- dt: {
- startDate: () => moment(0),
- suffix: '',
- },
-}
-
-module.exports = class Sourceforge extends BaseJsonService {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'sourceforge',
- pattern: ':interval(dt|dm|dw|dd)/:project/:folder*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'SourceForge',
- pattern: ':interval(dt|dm|dw|dd)/:project',
- namedParams: {
- interval: 'dm',
- project: 'sevenzip',
- },
- staticPreview: this.render({
- downloads: 215990,
- interval: 'dm',
- }),
- },
- {
- title: 'SourceForge',
- pattern: ':interval(dt|dm|dw|dd)/:project/:folder',
- namedParams: {
- interval: 'dm',
- project: 'arianne',
- folder: 'stendhal',
- },
- staticPreview: this.render({
- downloads: 550,
- interval: 'dm',
- }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'sourceforge' }
- }
-
- static render({ downloads, interval }) {
- return {
- label: 'downloads',
- message: `${metric(downloads)}${intervalMap[interval].suffix}`,
- color: downloadCount(downloads),
- }
- }
-
- async fetch({ interval, project, folder }) {
- const url = `https://sourceforge.net/projects/${project}/files/${
- folder ? `${folder}/` : ''
- }stats/json`
- // get yesterday since today is incomplete
- const endDate = moment().subtract(24, 'hours')
- const startDate = intervalMap[interval].startDate(endDate)
- const options = {
- qs: {
- start_date: startDate.format('YYYY-MM-DD'),
- end_date: endDate.format('YYYY-MM-DD'),
- },
- }
-
- return this._requestJson({
- schema,
- url,
- options,
- errorMessages: {
- 404: 'project not found',
- },
- })
- }
-
- async handle({ interval, project, folder }) {
- const json = await this.fetch({ interval, project, folder })
- return this.constructor.render({ interval, downloads: json.total })
- }
-}
diff --git a/services/sourceforge/sourceforge.tester.js b/services/sourceforge/sourceforge.tester.js
deleted file mode 100644
index 4dfa19bccd671..0000000000000
--- a/services/sourceforge/sourceforge.tester.js
+++ /dev/null
@@ -1,53 +0,0 @@
-'use strict'
-
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('total downloads')
- .get('/dt/sevenzip.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
-
-t.create('total downloads (with subdirs)')
- .get('/dt/smartmontools/smartmontools/7.1.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
-
-t.create('monthly downloads')
- .get('/dm/sevenzip.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
-
-t.create('weekly downloads')
- .get('/dw/sevenzip.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
-
-t.create('daily downloads')
- .get('/dd/sevenzip.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
-
-t.create('downloads folder')
- .get('/dm/arianne/stendhal.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
-
-t.create('invalid project')
- .get('/dd/invalid.json')
- .expectBadge({
- label: 'sourceforge',
- message: 'project not found',
- })
diff --git a/services/sourcegraph/sourcegraph.service.js b/services/sourcegraph/sourcegraph.service.js
index 0b2b5b82e8d63..6cbbdf6da0f49 100644
--- a/services/sourcegraph/sourcegraph.service.js
+++ b/services/sourcegraph/sourcegraph.service.js
@@ -1,43 +1,32 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, pathParams } from '../index.js'
const projectsCountRegex = /^\s[0-9]*(\.[0-9]k)?\sprojects$/
const schema = Joi.object({
- value: Joi.string()
- .regex(projectsCountRegex)
- .required(),
+ value: Joi.string().regex(projectsCountRegex).required(),
}).required()
-module.exports = class Sourcegraph extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class Sourcegraph extends BaseJsonService {
+ static category = 'other'
- static get route() {
- return {
- base: 'sourcegraph/rrc',
- pattern: ':repo(.*?)',
- }
+ static route = {
+ base: 'sourcegraph/rrc',
+ pattern: ':repo(.*?)',
}
- static get examples() {
- return [
- {
- title: 'Sourcegraph for Repo Reference Count',
- pattern: ':repo',
- namedParams: {
- repo: 'github.com/gorilla/mux',
- },
- staticPreview: this.render({ projectsCount: '9.9k projects' }),
+ static openApi = {
+ '/sourcegraph/rrc/{repo}': {
+ get: {
+ summary: 'Sourcegraph for Repo Reference Count',
+ parameters: pathParams({
+ name: 'repo',
+ example: 'github.com/gorilla/mux',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { color: 'brightgreen', label: 'used by' }
- }
+ static defaultBadgeData = { color: 'brightgreen', label: 'used by' }
static render({ projectsCount }) {
return {
diff --git a/services/sourcegraph/sourcegraph.tester.js b/services/sourcegraph/sourcegraph.tester.js
index 1f4bd9a2ee6c4..4d1428674354e 100644
--- a/services/sourcegraph/sourcegraph.tester.js
+++ b/services/sourcegraph/sourcegraph.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// Matches API responses such as "0 projects", "1 projects", "182 projects", "14.0k projects".
// There may be other cases not covered by this regex, but hopefully the tested projects won't vary much.
diff --git a/services/spack/spack.service.js b/services/spack/spack.service.js
index ed5ad36f1cfe6..5548548f6e28f 100644
--- a/services/spack/spack.service.js
+++ b/services/spack/spack.service.js
@@ -1,49 +1,41 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('..//version')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '..//version.js'
+import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
latest_version: Joi.string().required(),
}).required()
-module.exports = class SpackVersion extends BaseJsonService {
- static get category() {
- return 'version'
- }
+export default class SpackVersion extends BaseJsonService {
+ static category = 'version'
- static get route() {
- return {
- base: 'spack/v',
- pattern: ':packageName',
- }
+ static route = {
+ base: 'spack/v',
+ pattern: ':packageName',
}
- static get examples() {
- return [
- {
- title: 'Spack',
- namedParams: { packageName: 'adios2' },
- staticPreview: this.render({ version: '2.3.1' }),
- keywords: ['hpc'],
+ static openApi = {
+ '/spack/v/{packageName}': {
+ get: {
+ summary: 'Spack',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'adios2',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'spack' }
- }
+ static defaultBadgeData = { label: 'spack' }
static render({ version }) {
return renderVersionBadge({ version })
}
async fetch({ packageName }) {
- const firstLetter = packageName[0]
return this._requestJson({
schema,
- url: `https://packages.spack.io/api/${firstLetter}/${packageName}.json`,
- errorMessages: {
+ url: `https://packages.spack.io/data/packages/${packageName}.json`,
+ httpErrors: {
404: 'package not found',
},
})
diff --git a/services/spack/spack.tester.js b/services/spack/spack.tester.js
index e7ba5d63645a9..6a9fe7d38ef14 100644
--- a/services/spack/spack.tester.js
+++ b/services/spack/spack.tester.js
@@ -1,14 +1,11 @@
-'use strict'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('version (valid)')
- .get('/adios2.json')
- .expectBadge({
- label: 'spack',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('version (valid)').get('/adios2.json').expectBadge({
+ label: 'spack',
+ message: isVPlusDottedVersionAtLeastOne,
+})
t.create('version (not found)')
.get('/not-a-package.json')
diff --git a/services/spiget/spiget-base.js b/services/spiget/spiget-base.js
index 078f633666d9e..39ccc6254563e 100644
--- a/services/spiget/spiget-base.js
+++ b/services/spiget/spiget-base.js
@@ -1,16 +1,12 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
const resourceSchema = Joi.object({
downloads: Joi.number().required(),
file: Joi.object({
type: Joi.string().required(),
size: Joi.number().required(),
- sizeUnit: Joi.string()
- .allow('')
- .required(),
+ sizeUnit: Joi.string().allow('').required(),
}).required(),
testedVersions: Joi.array(),
rating: Joi.object({
@@ -19,12 +15,11 @@ const resourceSchema = Joi.object({
}).required(),
}).required()
-const documentation = `
+const description = `
+Spiget holds information about SpigotMC Resources, Plugins and Authors.
You can find your resource ID in the url for your resource page.
Example: https://www.spigotmc.org/resources/essentialsx.9089/ - Here the Resource ID is 9089.
`
-const keywords = ['spigot', 'spigotmc']
-
class BaseSpigetService extends BaseJsonService {
async fetch({
resourceId,
@@ -38,4 +33,4 @@ class BaseSpigetService extends BaseJsonService {
}
}
-module.exports = { keywords, documentation, BaseSpigetService }
+export { description, BaseSpigetService }
diff --git a/services/spiget/spiget-download-size.service.js b/services/spiget/spiget-download-size.service.js
index 3d74cf28259b3..ed6337367b0ae 100644
--- a/services/spiget/spiget-download-size.service.js
+++ b/services/spiget/spiget-download-size.service.js
@@ -1,44 +1,36 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { BaseSpigetService, description } from './spiget-base.js'
-const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+export default class SpigetDownloadSize extends BaseSpigetService {
+ static category = 'size'
-module.exports = class SpigetDownloadSize extends BaseSpigetService {
- static get category() {
- return 'size'
+ static route = {
+ base: 'spiget/download-size',
+ pattern: ':resourceId',
}
- static get route() {
- return {
- base: 'spiget/download-size',
- pattern: ':resourceId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Spiget Download Size',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: this.render({ size: 2.5, unit: 'MB' }),
- documentation,
- keywords,
+ static openApi = {
+ '/spiget/download-size/{resourceId}': {
+ get: {
+ summary: 'Spiget Download Size',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '15904',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'size',
- color: 'blue',
- }
+ static defaultBadgeData = {
+ label: 'size',
+ color: 'blue',
}
static render({ size, unit, type }) {
if (type === 'external') {
return {
- message: `resource hosted externally`,
+ message: 'resource hosted externally',
color: 'lightgrey',
}
}
diff --git a/services/spiget/spiget-download-size.tester.js b/services/spiget/spiget-download-size.tester.js
index a5c943d64222a..aba190e865c69 100644
--- a/services/spiget/spiget-download-size.tester.js
+++ b/services/spiget/spiget-download-size.tester.js
@@ -1,22 +1,17 @@
-'use strict'
+import { isMetricFileSize } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isFileSize } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('EssentialsX (hosted resource)')
+ .get('/771.json')
+ .expectBadge({ label: 'size', message: isMetricFileSize })
-t.create('EssentialsX (id 9089)')
- .get('/9089.json')
- .expectBadge({ label: 'size', message: isFileSize })
+t.create('external resource').get('/9089.json').expectBadge({
+ label: 'size',
+ message: 'resource hosted externally',
+})
-t.create('Advanced Achievements (id 6239)')
- .get('/6239.json')
- .expectBadge({
- lavel: 'size',
- message: 'resource hosted externally',
- })
-
-t.create('Invalid Resource (id 1)')
- .get('/1.json')
- .expectBadge({
- label: 'size',
- message: 'not found',
- })
+t.create('Invalid Resource').get('/1.json').expectBadge({
+ label: 'size',
+ message: 'not found',
+})
diff --git a/services/spiget/spiget-downloads.service.js b/services/spiget/spiget-downloads.service.js
index d3f0f0dff3f64..60ef6a406577c 100644
--- a/services/spiget/spiget-downloads.service.js
+++ b/services/spiget/spiget-downloads.service.js
@@ -1,50 +1,32 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseSpigetService, description } from './spiget-base.js'
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+export default class SpigetDownloads extends BaseSpigetService {
+ static category = 'downloads'
-module.exports = class SpigetDownloads extends BaseSpigetService {
- static get category() {
- return 'downloads'
+ static route = {
+ base: 'spiget/downloads',
+ pattern: ':resourceId',
}
- static get route() {
- return {
- base: 'spiget/downloads',
- pattern: ':resourceId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Spiget Downloads',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: this.render({ downloads: 560891 }),
- documentation,
- keywords,
+ static openApi = {
+ '/spiget/downloads/{resourceId}': {
+ get: {
+ summary: 'Spiget Downloads',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '9089',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'downloads',
- }
- }
-
- static render({ downloads }) {
- return {
- message: metric(downloads),
- color: downloadCount(downloads),
- }
- }
+ static defaultBadgeData = { label: 'downloads' }
async handle({ resourceId }) {
const { downloads } = await this.fetch({ resourceId })
- return this.constructor.render({ downloads })
+ return renderDownloadsBadge({ downloads })
}
}
diff --git a/services/spiget/spiget-downloads.tester.js b/services/spiget/spiget-downloads.tester.js
index fb2f7a2d8a671..7b372012b7644 100644
--- a/services/spiget/spiget-downloads.tester.js
+++ b/services/spiget/spiget-downloads.tester.js
@@ -1,18 +1,13 @@
-'use strict'
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('EssentialsX (id 9089)').get('/9089.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
-t.create('EssentialsX (id 9089)')
- .get('/9089.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
-
-t.create('Invalid Resource (id 1)')
- .get('/1.json')
- .expectBadge({
- label: 'downloads',
- message: 'not found',
- })
+t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
diff --git a/services/spiget/spiget-latest-version.service.js b/services/spiget/spiget-latest-version.service.js
index da9717f5763d0..d237a81a5e0bb 100644
--- a/services/spiget/spiget-latest-version.service.js
+++ b/services/spiget/spiget-latest-version.service.js
@@ -1,45 +1,37 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseSpigetService, description } from './spiget-base.js'
const versionSchema = Joi.object({
downloads: Joi.number().required(),
name: Joi.string().required(),
}).required()
-module.exports = class SpigetLatestVersion extends BaseSpigetService {
- static get category() {
- return 'version'
- }
+export default class SpigetLatestVersion extends BaseSpigetService {
+ static category = 'version'
- static get route() {
- return {
- base: 'spiget/version',
- pattern: ':resourceId',
- }
+ static route = {
+ base: 'spiget/version',
+ pattern: ':resourceId',
}
- static get examples() {
- return [
- {
- title: 'Spiget Version',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: renderVersionBadge({ version: 2.1 }),
- documentation,
- keywords,
+ static openApi = {
+ '/spiget/version/{resourceId}': {
+ get: {
+ summary: 'Spiget Version',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '9089',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'spiget',
- color: 'blue',
- }
+ static defaultBadgeData = {
+ label: 'spiget',
+ color: 'blue',
}
async handle({ resourceId }) {
diff --git a/services/spiget/spiget-latest-version.tester.js b/services/spiget/spiget-latest-version.tester.js
index c088f072c62d9..bcc8f68fc9f38 100644
--- a/services/spiget/spiget-latest-version.tester.js
+++ b/services/spiget/spiget-latest-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
// Note that Spigot versions can be anything (including just a string), so we'll make sure it's not returning 'not found'
@@ -12,9 +11,7 @@ t.create('EssentialsX (id 9089)')
message: withRegex(/^(?!not found$)/),
})
-t.create('Invalid Resource (id 1)')
- .get('/1.json')
- .expectBadge({
- label: 'spiget',
- message: 'not found',
- })
+t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({
+ label: 'spiget',
+ message: 'not found',
+})
diff --git a/services/spiget/spiget-rating.service.js b/services/spiget/spiget-rating.service.js
index 601db2a10bbf7..0ab0097fd33cf 100644
--- a/services/spiget/spiget-rating.service.js
+++ b/services/spiget/spiget-rating.service.js
@@ -1,51 +1,38 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { starRating, metric } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { BaseSpigetService, description } from './spiget-base.js'
-const { starRating, metric } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+export default class SpigetRatings extends BaseSpigetService {
+ static category = 'rating'
-module.exports = class SpigetRatings extends BaseSpigetService {
- static get category() {
- return 'rating'
+ static route = {
+ base: 'spiget',
+ pattern: ':format(rating|stars)/:resourceId',
}
- static get route() {
- return {
- base: 'spiget',
- pattern: ':format(rating|stars)/:resourceId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Spiget Stars',
- pattern: 'stars/:resourceId',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: this.render({
- format: 'stars',
- total: 325,
- average: 4.5,
- }),
- documentation,
- },
- {
- title: 'Spiget Rating',
- pattern: 'rating/:resourceId',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: this.render({ total: 325, average: 4.5 }),
- documentation,
- keywords,
+ static openApi = {
+ '/spiget/{format}/{resourceId}': {
+ get: {
+ summary: 'Spiget Rating',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ example: 'rating',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ },
+ {
+ name: 'resourceId',
+ example: '9089',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'rating' }
+ static defaultBadgeData = {
+ label: 'rating',
}
static render({ format, total, average }) {
@@ -64,7 +51,7 @@ module.exports = class SpigetRatings extends BaseSpigetService {
return this.constructor.render({
format,
total: rating.count,
- average: rating.average,
+ average: rating.average.toFixed(2),
})
}
}
diff --git a/services/spiget/spiget-rating.tester.js b/services/spiget/spiget-rating.tester.js
index fd238c0adbd95..7d44add2e58d2 100644
--- a/services/spiget/spiget-rating.tester.js
+++ b/services/spiget/spiget-rating.tester.js
@@ -1,21 +1,16 @@
-'use strict'
+import { isStarRating, withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isStarRating, withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+t.create('Stars - EssentialsX (id 9089)').get('/stars/9089.json').expectBadge({
+ label: 'rating',
+ message: isStarRating,
+})
-t.create('Stars - EssentialsX (id 9089)')
- .get('/stars/9089.json')
- .expectBadge({
- label: 'rating',
- message: isStarRating,
- })
-
-t.create('Stars - Invalid Resource (id 1)')
- .get('/stars/1.json')
- .expectBadge({
- label: 'rating',
- message: 'not found',
- })
+t.create('Stars - Invalid Resource (id 1)').get('/stars/1.json').expectBadge({
+ label: 'rating',
+ message: 'not found',
+})
t.create('Rating - EssentialsX (id 9089)')
.get('/rating/9089.json')
@@ -24,9 +19,7 @@ t.create('Rating - EssentialsX (id 9089)')
message: withRegex(/^(\d*\.\d+)(\/5 \()(\d+)(\))$/),
})
-t.create('Rating - Invalid Resource (id 1)')
- .get('/rating/1.json')
- .expectBadge({
- label: 'rating',
- message: 'not found',
- })
+t.create('Rating - Invalid Resource (id 1)').get('/rating/1.json').expectBadge({
+ label: 'rating',
+ message: 'not found',
+})
diff --git a/services/spiget/spiget-tested-versions.service.js b/services/spiget/spiget-tested-versions.service.js
index 88e2c3b99ad84..30835ccdd2e73 100644
--- a/services/spiget/spiget-tested-versions.service.js
+++ b/services/spiget/spiget-tested-versions.service.js
@@ -1,38 +1,30 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { BaseSpigetService, description } from './spiget-base.js'
-const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+export default class SpigetTestedVersions extends BaseSpigetService {
+ static category = 'platform-support'
-module.exports = class SpigetTestedVersions extends BaseSpigetService {
- static get category() {
- return 'platform-support'
+ static route = {
+ base: 'spiget/tested-versions',
+ pattern: ':resourceId',
}
- static get route() {
- return {
- base: 'spiget/tested-versions',
- pattern: ':resourceId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Spiget tested server versions',
- namedParams: {
- resourceId: '9089',
- },
- staticPreview: this.render({ versions: '1.7-1.13' }),
- documentation,
- keywords,
+ static openApi = {
+ '/spiget/tested-versions/{resourceId}': {
+ get: {
+ summary: 'Spiget tested server versions',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '9089',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'tested versions',
- color: 'blue',
- }
+ static defaultBadgeData = {
+ label: 'tested versions',
+ color: 'blue',
}
static render({ versions }) {
diff --git a/services/spiget/spiget-tested-versions.tester.js b/services/spiget/spiget-tested-versions.tester.js
index 7389e9283c996..130fffa97d832 100644
--- a/services/spiget/spiget-tested-versions.tester.js
+++ b/services/spiget/spiget-tested-versions.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const multipleVersions = withRegex(/^([+]?\d*\.\d+)(-)([+]?\d*\.\d+)$/)
@@ -12,12 +11,10 @@ t.create('EssentialsX - multiple versions supported - (id 9089)')
message: multipleVersions,
})
-t.create('Invalid Resource (id 1)')
- .get('/1.json')
- .expectBadge({
- label: 'tested versions',
- message: 'not found',
- })
+t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({
+ label: 'tested versions',
+ message: 'not found',
+})
t.create('Nock - single version supported')
.get('/1.json')
@@ -36,7 +33,7 @@ t.create('Nock - single version supported')
count: 1,
average: 1,
},
- })
+ }),
)
.expectBadge({
label: 'tested versions',
@@ -60,7 +57,7 @@ t.create('Nock - multiple versions supported')
count: 1,
average: 1,
},
- })
+ }),
)
.expectBadge({
label: 'tested versions',
diff --git a/services/stackexchange/stackexchange-base.js b/services/stackexchange/stackexchange-base.js
new file mode 100644
index 0000000000000..18c9ead9c24c2
--- /dev/null
+++ b/services/stackexchange/stackexchange-base.js
@@ -0,0 +1,37 @@
+import { BaseJsonService } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+
+export function renderQuestionsBadge({
+ suffix,
+ stackexchangesite,
+ query,
+ numValue,
+}) {
+ const label = `${stackexchangesite} ${query} questions`
+ return {
+ label,
+ message: `${metric(numValue)}${suffix}`,
+ color: floorCountColor(numValue, 1000, 10000, 20000),
+ }
+}
+
+export class StackExchangeBase extends BaseJsonService {
+ static category = 'chat'
+
+ static auth = {
+ passKey: 'stackapps_api_key',
+ authorizedOrigins: ['https://api.stackexchange.com'],
+ isRequired: false,
+ }
+
+ static defaultBadgeData = {
+ label: 'stackoverflow',
+ }
+
+ async fetch(params) {
+ return this._requestJson(
+ this.authHelper.withQueryStringAuth({ passKey: 'key' }, params),
+ )
+ }
+}
diff --git a/services/stackexchange/stackexchange-helpers.js b/services/stackexchange/stackexchange-helpers.js
deleted file mode 100644
index edb85404beeef..0000000000000
--- a/services/stackexchange/stackexchange-helpers.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-
-module.exports = function renderQuestionsBadge({
- suffix,
- stackexchangesite,
- query,
- numValue,
-}) {
- const label = `${stackexchangesite} ${query} questions`
- return {
- label,
- message: `${metric(numValue)}${suffix}`,
- color: floorCountColor(numValue, 1000, 10000, 20000),
- }
-}
diff --git a/services/stackexchange/stackexchange-monthlyquestions.service.js b/services/stackexchange/stackexchange-monthlyquestions.service.js
index c0d92c1de5863..4c164a4d73664 100644
--- a/services/stackexchange/stackexchange-monthlyquestions.service.js
+++ b/services/stackexchange/stackexchange-monthlyquestions.service.js
@@ -1,44 +1,38 @@
-'use strict'
-
-const moment = require('moment')
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
-const renderQuestionsBadge = require('./stackexchange-helpers')
+import dayjs from 'dayjs'
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import {
+ renderQuestionsBadge,
+ StackExchangeBase,
+} from './stackexchange-base.js'
const tagSchema = Joi.object({
total: nonNegativeInteger,
}).required()
-module.exports = class StackExchangeMonthlyQuestions extends BaseJsonService {
- static get category() {
- return 'chat'
- }
-
- static get route() {
- return {
- base: 'stackexchange',
- pattern: ':stackexchangesite/qm/:query',
- }
+export default class StackExchangeMonthlyQuestions extends StackExchangeBase {
+ static route = {
+ base: 'stackexchange',
+ pattern: ':stackexchangesite/qm/:query',
}
- static get examples() {
- return [
- {
- title: 'Stack Exchange monthly questions',
- namedParams: { stackexchangesite: 'stackoverflow', query: 'momentjs' },
- staticPreview: this.render({
- stackexchangesite: 'stackoverflow',
- query: 'momentjs',
- numValue: 2000,
- }),
- keywords: ['stackexchange', 'stackoverflow'],
+ static openApi = {
+ '/stackexchange/{stackexchangesite}/qm/{query}': {
+ get: {
+ summary: 'Stack Exchange monthly questions',
+ parameters: pathParams(
+ {
+ name: 'stackexchangesite',
+ example: 'stackoverflow',
+ },
+ {
+ name: 'query',
+ example: 'javascript',
+ },
+ ),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'stackoverflow' }
+ },
}
static render(props) {
@@ -49,21 +43,21 @@ module.exports = class StackExchangeMonthlyQuestions extends BaseJsonService {
}
async handle({ stackexchangesite, query }) {
- const today = moment().toDate()
- const prevMonthStart = moment(today)
+ const today = dayjs().toDate()
+ const prevMonthStart = dayjs(today)
.subtract(1, 'months')
.startOf('month')
.unix()
- const prevMonthEnd = moment(today)
+ const prevMonthEnd = dayjs(today)
.subtract(1, 'months')
.endOf('month')
.unix()
- const parsedData = await this._requestJson({
+ const parsedData = await this.fetch({
schema: tagSchema,
options: {
- gzip: true,
- qs: {
+ decompress: true,
+ searchParams: {
site: stackexchangesite,
fromdate: prevMonthStart,
todate: prevMonthEnd,
@@ -71,7 +65,7 @@ module.exports = class StackExchangeMonthlyQuestions extends BaseJsonService {
tagged: query,
},
},
- url: `https://api.stackexchange.com/2.2/questions`,
+ url: 'https://api.stackexchange.com/2.2/questions',
})
const numValue = parsedData.total
diff --git a/services/stackexchange/stackexchange-monthlyquestions.spec.js b/services/stackexchange/stackexchange-monthlyquestions.spec.js
new file mode 100644
index 0000000000000..b014718eaefc0
--- /dev/null
+++ b/services/stackexchange/stackexchange-monthlyquestions.spec.js
@@ -0,0 +1,17 @@
+import { testAuth } from '../test-helpers.js'
+import StackExchangeMonthlyQuestions from './stackexchange-monthlyquestions.service.js'
+
+describe('StackExchangeMonthlyQuestions', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ StackExchangeMonthlyQuestions,
+ 'QueryStringAuth',
+ {
+ total: 8,
+ },
+ { queryPassKey: 'key' },
+ )
+ })
+ })
+})
diff --git a/services/stackexchange/stackexchange-monthlyquestions.tester.js b/services/stackexchange/stackexchange-monthlyquestions.tester.js
index 88f8b16622c31..766703a967908 100644
--- a/services/stackexchange/stackexchange-monthlyquestions.tester.js
+++ b/services/stackexchange/stackexchange-monthlyquestions.tester.js
@@ -1,12 +1,11 @@
-'use strict'
+import { isMetricOverTimePeriod } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
-const { isMetricOverTimePeriod } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Monthly Questions for StackOverflow Momentjs')
- .get('/stackoverflow/qm/momentjs.json')
+t.create('Monthly Questions for StackOverflow javascript')
+ .get('/stackoverflow/qm/javascript.json')
.expectBadge({
- label: 'stackoverflow momentjs questions',
+ label: 'stackoverflow javascript questions',
message: isMetricOverTimePeriod,
})
diff --git a/services/stackexchange/stackexchange-reputation.service.js b/services/stackexchange/stackexchange-reputation.service.js
index 0d00d8c4bf65e..2ee06a3d3a607 100644
--- a/services/stackexchange/stackexchange-reputation.service.js
+++ b/services/stackexchange/stackexchange-reputation.service.js
@@ -1,51 +1,42 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { StackExchangeBase } from './stackexchange-base.js'
const reputationSchema = Joi.object({
items: Joi.array()
.length(1)
.items(
Joi.object({
- reputation: Joi.number()
- .min(0)
- .required(),
- })
+ reputation: Joi.number().min(0).required(),
+ }),
)
.required(),
}).required()
-module.exports = class StackExchangeReputation extends BaseJsonService {
- static get category() {
- return 'chat'
+export default class StackExchangeReputation extends StackExchangeBase {
+ static route = {
+ base: 'stackexchange',
+ pattern: ':stackexchangesite/r/:query',
}
- static get route() {
- return {
- base: 'stackexchange',
- pattern: ':stackexchangesite/r/:query',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Stack Exchange reputation',
- namedParams: { stackexchangesite: 'stackoverflow', query: '123' },
- staticPreview: this.render({
- stackexchangesite: 'stackoverflow',
- numValue: 10,
- }),
- keywords: ['stackexchange', 'stackoverflow'],
+ static openApi = {
+ '/stackexchange/{stackexchangesite}/r/{query}': {
+ get: {
+ summary: 'Stack Exchange reputation',
+ parameters: pathParams(
+ {
+ name: 'stackexchangesite',
+ example: 'stackoverflow',
+ },
+ {
+ name: 'query',
+ example: '123',
+ },
+ ),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'stackoverflow' }
+ },
}
static render({ stackexchangesite, numValue }) {
@@ -61,11 +52,11 @@ module.exports = class StackExchangeReputation extends BaseJsonService {
async handle({ stackexchangesite, query }) {
const path = `users/${query}`
- const parsedData = await this._requestJson({
+ const parsedData = await this.fetch({
schema: reputationSchema,
- options: { gzip: true, qs: { site: stackexchangesite } },
+ options: { decompress: true, searchParams: { site: stackexchangesite } },
url: `https://api.stackexchange.com/2.2/${path}`,
- errorMessages: {
+ httpErrors: {
400: 'invalid parameters',
},
})
diff --git a/services/stackexchange/stackexchange-reputation.spec.js b/services/stackexchange/stackexchange-reputation.spec.js
new file mode 100644
index 0000000000000..b0bcd9be17732
--- /dev/null
+++ b/services/stackexchange/stackexchange-reputation.spec.js
@@ -0,0 +1,17 @@
+import { testAuth } from '../test-helpers.js'
+import StackExchangeReputation from './stackexchange-reputation.service.js'
+
+describe('StackExchangeReputation', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ StackExchangeReputation,
+ 'QueryStringAuth',
+ {
+ items: [{ reputation: 8 }],
+ },
+ { queryPassKey: 'key' },
+ )
+ })
+ })
+})
diff --git a/services/stackexchange/stackexchange-reputation.tester.js b/services/stackexchange/stackexchange-reputation.tester.js
index 18a1f8144af74..12c043f10c7f1 100644
--- a/services/stackexchange/stackexchange-reputation.tester.js
+++ b/services/stackexchange/stackexchange-reputation.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Invalid parameters')
.get('/stackoverflow/r/invalidimage.json')
@@ -14,9 +13,7 @@ t.create('Reputation for StackOverflow user 22656')
message: isMetric,
})
-t.create('Reputation for Tex user 22656')
- .get('/tex/r/226.json')
- .expectBadge({
- label: 'tex reputation',
- message: isMetric,
- })
+t.create('Reputation for Tex user 22656').get('/tex/r/226.json').expectBadge({
+ label: 'tex reputation',
+ message: isMetric,
+})
diff --git a/services/stackexchange/stackexchange-taginfo.service.js b/services/stackexchange/stackexchange-taginfo.service.js
index 9736e85bffdab..dacb79eafa65e 100644
--- a/services/stackexchange/stackexchange-taginfo.service.js
+++ b/services/stackexchange/stackexchange-taginfo.service.js
@@ -1,51 +1,43 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-const renderQuestionsBadge = require('./stackexchange-helpers')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import {
+ renderQuestionsBadge,
+ StackExchangeBase,
+} from './stackexchange-base.js'
const tagSchema = Joi.object({
items: Joi.array()
.length(1)
.items(
Joi.object({
- count: Joi.number()
- .min(0)
- .required(),
- })
+ count: Joi.number().min(0).required(),
+ }),
)
.required(),
}).required()
-module.exports = class StackExchangeQuestions extends BaseJsonService {
- static get category() {
- return 'chat'
- }
-
- static get route() {
- return {
- base: 'stackexchange',
- pattern: ':stackexchangesite/t/:query',
- }
+export default class StackExchangeQuestions extends StackExchangeBase {
+ static route = {
+ base: 'stackexchange',
+ pattern: ':stackexchangesite/t/:query',
}
- static get examples() {
- return [
- {
- title: 'Stack Exchange questions',
- namedParams: { stackexchangesite: 'stackoverflow', query: 'gson' },
- staticPreview: this.render({
- stackexchangesite: 'stackoverflow',
- query: 'gson',
- numValue: 10,
- }),
- keywords: ['stackexchange', 'stackoverflow'],
+ static openApi = {
+ '/stackexchange/{stackexchangesite}/t/{query}': {
+ get: {
+ summary: 'Stack Exchange questions',
+ parameters: pathParams(
+ {
+ name: 'stackexchangesite',
+ example: 'stackoverflow',
+ },
+ {
+ name: 'query',
+ example: 'gson',
+ },
+ ),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'stackoverflow' }
+ },
}
static render(props) {
@@ -58,9 +50,9 @@ module.exports = class StackExchangeQuestions extends BaseJsonService {
async handle({ stackexchangesite, query }) {
const path = `tags/${query}/info`
- const parsedData = await this._requestJson({
+ const parsedData = await this.fetch({
schema: tagSchema,
- options: { gzip: true, qs: { site: stackexchangesite } },
+ options: { decompress: true, searchParams: { site: stackexchangesite } },
url: `https://api.stackexchange.com/2.2/${path}`,
})
diff --git a/services/stackexchange/stackexchange-taginfo.spec.js b/services/stackexchange/stackexchange-taginfo.spec.js
new file mode 100644
index 0000000000000..46977c1ced29b
--- /dev/null
+++ b/services/stackexchange/stackexchange-taginfo.spec.js
@@ -0,0 +1,17 @@
+import { testAuth } from '../test-helpers.js'
+import StackExchangeQuestions from './stackexchange-taginfo.service.js'
+
+describe('StackExchangeQuestions', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ StackExchangeQuestions,
+ 'QueryStringAuth',
+ {
+ items: [{ count: 8 }],
+ },
+ { queryPassKey: 'key' },
+ )
+ })
+ })
+})
diff --git a/services/stackexchange/stackexchange-taginfo.tester.js b/services/stackexchange/stackexchange-taginfo.tester.js
index e80b518ce3a0c..9c615fb693a49 100644
--- a/services/stackexchange/stackexchange-taginfo.tester.js
+++ b/services/stackexchange/stackexchange-taginfo.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isMetric } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('JavaScript Questions')
.get('/stackoverflow/t/javascript.json')
diff --git a/services/static-badge/query-string-static.service.js b/services/static-badge/query-string-static.service.js
index 185ed358784e3..1555e55bcf777 100644
--- a/services/static-badge/query-string-static.service.js
+++ b/services/static-badge/query-string-static.service.js
@@ -1,25 +1,19 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseStaticService } = require('..')
+import Joi from 'joi'
+import { BaseStaticService } from '../index.js'
const queryParamSchema = Joi.object({
message: Joi.string().required(),
}).required()
-module.exports = class QueryStringStaticBadge extends BaseStaticService {
- static get category() {
- return 'static'
- }
+export default class QueryStringStaticBadge extends BaseStaticService {
+ static category = 'static'
- static get route() {
- return {
- base: '',
- pattern: 'static/:schemaVersion(v1)',
- // All but one of the parameters are parsed via coalesceBadge. This
- // reuses what is the override behaviour for other badges.
- queryParamSchema,
- }
+ static route = {
+ base: '',
+ pattern: 'static/:schemaVersion(v1)',
+ // All but one of the parameters are parsed via coalesceBadge. This
+ // reuses what is the override behaviour for other badges.
+ queryParamSchema,
}
handle(namedParams, queryParams) {
diff --git a/services/static-badge/query-string-static.tester.js b/services/static-badge/query-string-static.tester.js
index 2e1e7602a6bb8..b6609c250521b 100644
--- a/services/static-badge/query-string-static.tester.js
+++ b/services/static-badge/query-string-static.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Missing message')
.get('/static/v1.json?label=label&message=&color=blue')
diff --git a/services/static-badge/static-badge.service.js b/services/static-badge/static-badge.service.js
index 44c013d1f8be5..910a79f962b8d 100644
--- a/services/static-badge/static-badge.service.js
+++ b/services/static-badge/static-badge.service.js
@@ -1,19 +1,88 @@
-'use strict'
+import { escapeFormat } from '../../core/badge-urls/path-helpers.js'
+import { BaseStaticService } from '../index.js'
-const { escapeFormat } = require('../../core/badge-urls/path-helpers')
-const { BaseStaticService } = require('..')
+const description = `
+The static badge accepts a single required path parameter which encodes either:
-module.exports = class StaticBadge extends BaseStaticService {
- static get category() {
- return 'static'
+
+
+ Label, message and color separated by a dash -. For example:
+ -
+ https://img.shields.io/badge/any_text-you_like-blue
+
+
+ Message and color only, separated by a dash -. For example:
+ -
+ https://img.shields.io/badge/just%20the%20message-8A2BE2
+
+
+
+
+
+
+ URL input
+ Badge output
+
+
+ Underscore _ or %20
+ Space
+
+
+ Double underscore __
+ Underscore _
+
+
+ Double dash --
+ Dash -
+
+
+
+
+Examples of additional options:
+
+
+ Style: -
+ https://img.shields.io/badge/build-passing-brightgreen?style=for-the-badge
+
+
+ Color (named): -
+ https://img.shields.io/badge/coverage-95%25-orange
+
+
+ Logo: -
+ https://img.shields.io/badge/github-repo-blue?logo=github
+
+
+
+Hex, rgb, rgba, hsl, hsla and css named colors may be used. Note: Some named colors may differ from CSS color values. For a list of named colors, see the badge-maker readme .
+`
+
+export default class StaticBadge extends BaseStaticService {
+ static category = 'static'
+ static route = {
+ base: '',
+ format: '(?::|badge/)((?:[^-]|--)*?)-?((?:[^-]|--)*)-((?:[^-.]|--)+)',
+ capture: ['label', 'message', 'color'],
}
- static get route() {
- return {
- base: '',
- format: '(?::|badge/)((?:[^-]|--)*?)-?((?:[^-]|--)*)-((?:[^-.]|--)+)',
- capture: ['label', 'message', 'color'],
- }
+ static openApi = {
+ '/badge/{badgeContent}': {
+ get: {
+ summary: 'Static Badge',
+ description,
+ parameters: [
+ {
+ name: 'badgeContent',
+ description:
+ 'Label, (optional) message, and color. Separated by dashes',
+ in: 'path',
+ required: true,
+ schema: { type: 'string' },
+ example: 'build-passing-brightgreen',
+ },
+ ],
+ },
+ },
}
handle({ label, message, color }) {
diff --git a/services/static-badge/static-badge.tester.js b/services/static-badge/static-badge.tester.js
index 1d3b4e25c4fc8..dc0b0218c0348 100644
--- a/services/static-badge/static-badge.tester.js
+++ b/services/static-badge/static-badge.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Shields colorscheme color')
.get('/badge/label-message-blue.json')
diff --git a/services/steam/steam-base.js b/services/steam/steam-base.js
index 4c2951c4c90f8..459c6d2a7ac0f 100644
--- a/services/steam/steam-base.js
+++ b/services/steam/steam-base.js
@@ -1,9 +1,8 @@
-'use strict'
/**
* @module
*/
-const { BaseJsonService } = require('..')
+import { BaseJsonService } from '../index.js'
/**
* The steam api is based like /{interface}/{method}/v{version}/
@@ -46,7 +45,7 @@ class BaseSteamAPI extends BaseJsonService {
return this._requestJson({
url,
schema,
- errorMessages: {
+ httpErrors: {
400: 'bad request',
},
options,
@@ -54,4 +53,4 @@ class BaseSteamAPI extends BaseJsonService {
}
}
-module.exports = BaseSteamAPI
+export default BaseSteamAPI
diff --git a/services/steam/steam-workshop.service.js b/services/steam/steam-workshop.service.js
index d53598d2768c3..a1024f81e7363 100644
--- a/services/steam/steam-workshop.service.js
+++ b/services/steam/steam-workshop.service.js
@@ -1,25 +1,20 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { metric, formatDate } = require('../text-formatters')
-const { age: ageColor, downloadCount } = require('../color-formatters')
-const { NotFound } = require('..')
-const BaseSteamAPI = require('./steam-base')
-
-const documentation = `
-
- Using a web browser, you can find the ID in the url here:
-
+import Joi from 'joi'
+import { renderDateBadge } from '../date.js'
+import { renderSizeBadge } from '../size.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { metric } from '../text-formatters.js'
+import { NotFound, pathParams } from '../index.js'
+import BaseSteamAPI from './steam-base.js'
+
+const description = `
+Using a web browser, you can find the ID in the url here:
-
- In the steam client you can simply just Right-Click and 'Copy Page URL' and follow the above step
-
+In the steam client you can simply just Right-Click and 'Copy Page URL' and follow the above step
+ alt="Right-Click and 'Copy Page URL'" />
`
const steamCollectionSchema = Joi.object({
@@ -29,7 +24,7 @@ const steamCollectionSchema = Joi.object({
.items(
Joi.object({
children: Joi.array().required(),
- }).required()
+ }).required(),
)
.required(),
})
@@ -42,12 +37,8 @@ const steamCollectionNotFoundSchema = Joi.object({
collectiondetails: Joi.array()
.items(
Joi.object({
- result: Joi.number()
- .integer()
- .min(9)
- .max(9)
- .required(),
- }).required()
+ result: Joi.number().integer().min(9).max(9).required(),
+ }).required(),
)
.required(),
})
@@ -56,7 +47,7 @@ const steamCollectionNotFoundSchema = Joi.object({
const collectionFoundOrNotSchema = Joi.alternatives(
steamCollectionSchema,
- steamCollectionNotFoundSchema
+ steamCollectionNotFoundSchema,
)
const steamFileSchema = Joi.object({
@@ -65,28 +56,15 @@ const steamFileSchema = Joi.object({
publishedfiledetails: Joi.array()
.items(
Joi.object({
- file_size: Joi.number()
- .integer()
- .required(),
- time_created: Joi.number()
- .integer()
- .required(),
- subscriptions: Joi.number()
- .integer()
- .required(),
- favorited: Joi.number()
- .integer()
- .required(),
- lifetime_subscriptions: Joi.number()
- .integer()
- .required(),
- lifetime_favorited: Joi.number()
- .integer()
- .required(),
- views: Joi.number()
- .integer()
- .required(),
- })
+ file_size: Joi.number().integer().required(),
+ time_created: Joi.number().integer().required(),
+ time_updated: Joi.number().integer().required(),
+ subscriptions: Joi.number().integer().required(),
+ favorited: Joi.number().integer().required(),
+ lifetime_subscriptions: Joi.number().integer().required(),
+ lifetime_favorited: Joi.number().integer().required(),
+ views: Joi.number().integer().required(),
+ }),
)
.min(1)
.max(1)
@@ -101,12 +79,8 @@ const steamFileNotFoundSchema = Joi.object({
publishedfiledetails: Joi.array()
.items(
Joi.object({
- result: Joi.number()
- .integer()
- .min(9)
- .max(9)
- .required(),
- }).required()
+ result: Joi.number().integer().min(9).max(9).required(),
+ }).required(),
)
.min(1)
.max(1)
@@ -117,51 +91,43 @@ const steamFileNotFoundSchema = Joi.object({
const fileFoundOrNotSchema = Joi.alternatives(
steamFileSchema,
- steamFileNotFoundSchema
+ steamFileNotFoundSchema,
)
class SteamCollectionSize extends BaseSteamAPI {
- static get category() {
- return 'other'
- }
+ static category = 'other'
- static get route() {
- return {
- base: 'steam/collection-files',
- pattern: ':collectionId',
- }
+ static route = {
+ base: 'steam/collection-files',
+ pattern: ':collectionId',
}
- static get examples() {
- return [
- {
- title: 'Steam Collection Files',
- namedParams: { collectionId: '180077636' },
- staticPreview: this.render({ size: 32 }),
- documentation,
+ static openApi = {
+ '/steam/collection-files/{collectionId}': {
+ get: {
+ summary: 'Steam Collection Files',
+ description,
+ parameters: pathParams({
+ name: 'collectionId',
+ example: '180077636',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'files' }
+ static defaultBadgeData = {
+ label: 'files',
}
static render({ size }) {
return { message: metric(size), color: 'brightgreen' }
}
- static get interf() {
- return 'ISteamRemoteStorage'
- }
+ static interf = 'ISteamRemoteStorage'
- static get method() {
- return 'GetCollectionDetails'
- }
+ static method = 'GetCollectionDetails'
- static get version() {
- return '1'
- }
+ static version = '1'
async handle({ collectionId }) {
const options = {
@@ -188,17 +154,11 @@ class SteamCollectionSize extends BaseSteamAPI {
}
class SteamFileService extends BaseSteamAPI {
- static get interf() {
- return 'ISteamRemoteStorage'
- }
+ static interf = 'ISteamRemoteStorage'
- static get method() {
- return 'GetPublishedFileDetails'
- }
+ static method = 'GetPublishedFileDetails'
- static get version() {
- return '1'
- }
+ static version = '1'
async onRequest({ response }) {
throw new Error(`onRequest() wasn't implemented for ${this.name}`)
@@ -224,105 +184,120 @@ class SteamFileService extends BaseSteamAPI {
}
class SteamFileSize extends SteamFileService {
- static get category() {
- return 'size'
- }
+ static category = 'size'
- static get route() {
- return {
- base: 'steam/size',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/size',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam File Size',
- namedParams: { fileId: '100' },
- staticPreview: this.render({ fileSize: 20000 }),
- documentation,
+ static openApi = {
+ '/steam/size/{fileId}': {
+ get: {
+ summary: 'Steam File Size',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'size' }
- }
-
- static render({ fileSize }) {
- return { message: prettyBytes(fileSize), color: 'brightgreen' }
+ static defaultBadgeData = {
+ label: 'size',
}
async onRequest({ response }) {
- return this.constructor.render({ fileSize: response.file_size })
+ return renderSizeBadge(response.file_size, 'metric')
}
}
class SteamFileReleaseDate extends SteamFileService {
- static get category() {
- return 'activity'
- }
+ static category = 'activity'
- static get route() {
- return {
- base: 'steam/release-date',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/release-date',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam Release Date',
- namedParams: { fileId: '100' },
- staticPreview: this.render({
- releaseDate: new Date(0).setUTCSeconds(1538288239),
+ static openApi = {
+ '/steam/release-date/{fileId}': {
+ get: {
+ summary: 'Steam Release Date',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
}),
- documentation,
},
- ]
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'release date',
}
- static get defaultBadgeData() {
- return { label: 'release date' }
+ async onRequest({ response }) {
+ const releaseDate = new Date(0).setUTCSeconds(response.time_created)
+ return renderDateBadge(releaseDate)
}
+}
- static render({ releaseDate }) {
- return { message: formatDate(releaseDate), color: ageColor(releaseDate) }
+class SteamFileUpdateDate extends SteamFileService {
+ static category = 'activity'
+
+ static route = {
+ base: 'steam/update-date',
+ pattern: ':fileId',
+ }
+
+ static openApi = {
+ '/steam/update-date/{fileId}': {
+ get: {
+ summary: 'Steam Update Date',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'update date',
}
async onRequest({ response }) {
- const releaseDate = new Date(0).setUTCSeconds(response.time_created)
- return this.constructor.render({ releaseDate })
+ const updateDate = new Date(0).setUTCSeconds(response.time_updated)
+ return renderDateBadge(updateDate)
}
}
class SteamFileSubscriptions extends SteamFileService {
- static get category() {
- return 'rating'
- }
+ static category = 'rating'
- static get route() {
- return {
- base: 'steam/subscriptions',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/subscriptions',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam Subscriptions',
- namedParams: { fileId: '100' },
- staticPreview: this.render({ subscriptions: 20124 }),
- documentation,
+ static openApi = {
+ '/steam/subscriptions/{fileId}': {
+ get: {
+ summary: 'Steam Subscriptions',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'subscriptions' }
+ static defaultBadgeData = {
+ label: 'subscriptions',
}
static render({ subscriptions }) {
@@ -335,30 +310,28 @@ class SteamFileSubscriptions extends SteamFileService {
}
class SteamFileFavorites extends SteamFileService {
- static get category() {
- return 'rating'
- }
+ static category = 'rating'
- static get route() {
- return {
- base: 'steam/favorites',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/favorites',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam Favorites',
- namedParams: { fileId: '100' },
- staticPreview: this.render({ favorites: 20000 }),
- documentation,
+ static openApi = {
+ '/steam/favorites/{fileId}': {
+ get: {
+ summary: 'Steam Favorites',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'favorites' }
+ static defaultBadgeData = {
+ label: 'favorites',
}
static render({ favorites }) {
@@ -371,68 +344,56 @@ class SteamFileFavorites extends SteamFileService {
}
class SteamFileDownloads extends SteamFileService {
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get route() {
- return {
- base: 'steam/downloads',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/downloads',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam Downloads',
- namedParams: { fileId: '100' },
- staticPreview: this.render({ downloads: 20124 }),
- documentation,
+ static openApi = {
+ '/steam/downloads/{fileId}': {
+ get: {
+ summary: 'Steam Downloads',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static defaultBadgeData = { label: 'downloads' }
- static render({ downloads }) {
- return { message: metric(downloads), color: downloadCount(downloads) }
- }
-
- async onRequest({ response }) {
- return this.constructor.render({
- downloads: response.lifetime_subscriptions,
- })
+ async onRequest({ response: { lifetime_subscriptions: downloads } }) {
+ return renderDownloadsBadge({ downloads })
}
}
class SteamFileViews extends SteamFileService {
- static get category() {
- return 'other'
- }
+ static category = 'other'
- static get route() {
- return {
- base: 'steam/views',
- pattern: ':fileId',
- }
+ static route = {
+ base: 'steam/views',
+ pattern: ':fileId',
}
- static get examples() {
- return [
- {
- title: 'Steam Views',
- namedParams: { fileId: '100' },
- staticPreview: this.render({ views: 20000 }),
- documentation,
+ static openApi = {
+ '/steam/views/{fileId}': {
+ get: {
+ summary: 'Steam Views',
+ description,
+ parameters: pathParams({
+ name: 'fileId',
+ example: '100',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'views' }
+ static defaultBadgeData = {
+ label: 'views',
}
static render({ views }) {
@@ -444,10 +405,11 @@ class SteamFileViews extends SteamFileService {
}
}
-module.exports = {
+export {
SteamCollectionSize,
SteamFileSize,
SteamFileReleaseDate,
+ SteamFileUpdateDate,
SteamFileSubscriptions,
SteamFileFavorites,
SteamFileDownloads,
diff --git a/services/steam/steam-workshop.tester.js b/services/steam/steam-workshop.tester.js
index 82178e5f534ab..578551e222990 100644
--- a/services/steam/steam-workshop.tester.js
+++ b/services/steam/steam-workshop.tester.js
@@ -1,10 +1,14 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-const { isMetric, isFileSize, isFormattedDate } = require('../test-validators')
-
-const t = new ServiceTester({ id: 'steam', title: 'Steam Workshop Tests' })
-module.exports = t
+import { ServiceTester } from '../tester.js'
+import {
+ isMetric,
+ isMetricFileSize,
+ isFormattedDate,
+} from '../test-validators.js'
+
+export const t = new ServiceTester({
+ id: 'steam',
+ title: 'Steam Workshop Tests',
+})
t.create('Collection Files')
.get('/collection-files/180077636.json')
@@ -12,12 +16,16 @@ t.create('Collection Files')
t.create('File Size')
.get('/size/1523924535.json')
- .expectBadge({ label: 'size', message: isFileSize })
+ .expectBadge({ label: 'size', message: isMetricFileSize })
t.create('Release Date')
.get('/release-date/1523924535.json')
.expectBadge({ label: 'release date', message: isFormattedDate })
+t.create('Update Date')
+ .get('/update-date/1523924535.json')
+ .expectBadge({ label: 'update date', message: isFormattedDate })
+
t.create('Subscriptions')
.get('/subscriptions/1523924535.json')
.expectBadge({ label: 'subscriptions', message: isMetric })
@@ -46,6 +54,10 @@ t.create('Release Date | File Not Found')
.get('/release-date/1.json')
.expectBadge({ label: 'release date', message: 'file not found' })
+t.create('Update Date | File Not Found')
+ .get('/update-date/1.json')
+ .expectBadge({ label: 'update date', message: 'file not found' })
+
t.create('Subscriptions | File Not Found')
.get('/subscriptions/1.json')
.expectBadge({ label: 'subscriptions', message: 'file not found' })
diff --git a/services/suggest.integration.js b/services/suggest.integration.js
deleted file mode 100644
index c805bb64b0314..0000000000000
--- a/services/suggest.integration.js
+++ /dev/null
@@ -1,276 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const Camp = require('@shields_io/camp')
-const portfinder = require('portfinder')
-const config = require('config').util.toObject()
-const got = require('../core/got-test-client')
-const { setRoutes } = require('./suggest')
-const GithubApiProvider = require('./github/github-api-provider')
-
-describe('Badge suggestions for', function() {
- const githubApiBaseUrl = process.env.GITHUB_URL || 'https://api.github.com'
-
- let token, apiProvider
- before(function() {
- token = config.private.gh_token
- if (!token) {
- throw Error('The integration tests require a gh_token to be set')
- }
- apiProvider = new GithubApiProvider({
- baseUrl: githubApiBaseUrl,
- globalToken: token,
- withPooling: false,
- })
- })
-
- let port, baseUrl
- before(async function() {
- port = await portfinder.getPortPromise()
- baseUrl = `http://127.0.0.1:${port}`
- })
-
- let camp
- before(async function() {
- camp = Camp.start({ port, hostname: '::' })
- await new Promise(resolve => camp.on('listening', () => resolve()))
- })
- after(async function() {
- if (camp) {
- await new Promise(resolve => camp.close(resolve))
- camp = undefined
- }
- })
-
- const origin = 'https://example.test'
- before(function() {
- setRoutes([origin], apiProvider, camp)
- })
- describe('GitHub', function() {
- context('with an existing project', function() {
- it('returns the expected suggestions', async function() {
- const { statusCode, body } = await got(
- `${baseUrl}/$suggest/v1?url=${encodeURIComponent(
- 'https://github.com/atom/atom'
- )}`,
- {
- json: true,
- }
- )
- expect(statusCode).to.equal(200)
- expect(body).to.deep.equal({
- suggestions: [
- {
- title: 'GitHub issues',
- link: 'https://github.com/atom/atom/issues',
- example: {
- pattern: '/github/issues/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub forks',
- link: 'https://github.com/atom/atom/network',
- example: {
- pattern: '/github/forks/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub stars',
- link: 'https://github.com/atom/atom/stargazers',
- example: {
- pattern: '/github/stars/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub license',
- link: 'https://github.com/atom/atom/blob/master/LICENSE.md',
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'Twitter',
- link:
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fatom%2Fatom',
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: {
- url: 'https://github.com/atom/atom',
- },
- },
- preview: {
- style: 'social',
- },
- },
- ],
- })
- })
- })
-
- context('with a non-existent project', function() {
- it('returns the expected suggestions', async function() {
- this.timeout(5000)
-
- const { statusCode, body } = await got(
- `${baseUrl}/$suggest/v1?url=${encodeURIComponent(
- 'https://github.com/badges/not-a-real-project'
- )}`,
- {
- json: true,
- }
- )
- expect(statusCode).to.equal(200)
- expect(body).to.deep.equal({
- suggestions: [
- {
- title: 'GitHub issues',
- link: 'https://github.com/badges/not-a-real-project/issues',
- example: {
- pattern: '/github/issues/:user/:repo',
- namedParams: { user: 'badges', repo: 'not-a-real-project' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub forks',
- link: 'https://github.com/badges/not-a-real-project/network',
- example: {
- pattern: '/github/forks/:user/:repo',
- namedParams: { user: 'badges', repo: 'not-a-real-project' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub stars',
- link: 'https://github.com/badges/not-a-real-project/stargazers',
- example: {
- pattern: '/github/stars/:user/:repo',
- namedParams: { user: 'badges', repo: 'not-a-real-project' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub license',
- link: 'https://github.com/badges/not-a-real-project',
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user: 'badges', repo: 'not-a-real-project' },
- queryParams: {},
- },
- },
- {
- title: 'Twitter',
- link:
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fbadges%2Fnot-a-real-project',
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: {
- url: 'https://github.com/badges/not-a-real-project',
- },
- },
- preview: {
- style: 'social',
- },
- },
- ],
- })
- })
- })
- })
-
- describe('GitLab', function() {
- context('with an existing project', function() {
- it('returns the expected suggestions', async function() {
- const { statusCode, body } = await got(
- `${baseUrl}/$suggest/v1?url=${encodeURIComponent(
- 'https://gitlab.com/gitlab-org/gitlab'
- )}`,
- {
- json: true,
- }
- )
- expect(statusCode).to.equal(200)
- expect(body).to.deep.equal({
- suggestions: [
- {
- title: 'GitLab pipeline',
- link: 'https://gitlab.com/gitlab-org/gitlab/builds',
- example: {
- pattern: '/gitlab/pipeline/:user/:repo',
- namedParams: { user: 'gitlab-org', repo: 'gitlab' },
- queryParams: {},
- },
- },
- {
- title: 'Twitter',
- link:
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgitlab.com%2Fgitlab-org%2Fgitlab',
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: {
- url: 'https://gitlab.com/gitlab-org/gitlab',
- },
- },
- preview: {
- style: 'social',
- },
- },
- ],
- })
- })
- })
-
- context('with an nonexisting project', function() {
- it('returns the expected suggestions', async function() {
- const { statusCode, body } = await got(
- `${baseUrl}/$suggest/v1?url=${encodeURIComponent(
- 'https://gitlab.com/gitlab-org/not-gitlab'
- )}`,
- {
- json: true,
- }
- )
- expect(statusCode).to.equal(200)
- expect(body).to.deep.equal({
- suggestions: [
- {
- title: 'GitLab pipeline',
- link: 'https://gitlab.com/gitlab-org/not-gitlab/builds',
- example: {
- pattern: '/gitlab/pipeline/:user/:repo',
- namedParams: { user: 'gitlab-org', repo: 'not-gitlab' },
- queryParams: {},
- },
- },
- {
- title: 'Twitter',
- link:
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgitlab.com%2Fgitlab-org%2Fnot-gitlab',
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: {
- url: 'https://gitlab.com/gitlab-org/not-gitlab',
- },
- },
- preview: {
- style: 'social',
- },
- },
- ],
- })
- })
- })
- })
-})
diff --git a/services/suggest.js b/services/suggest.js
deleted file mode 100644
index ac1c6893939a2..0000000000000
--- a/services/suggest.js
+++ /dev/null
@@ -1,206 +0,0 @@
-// Suggestion API
-//
-// eg. /$suggest/v1?url=https://github.com/badges/shields
-//
-// This endpoint is called from frontend/components/suggestion-and-search.js.
-
-'use strict'
-
-const { URL } = require('url')
-const request = require('request')
-
-function twitterPage(url) {
- if (url.protocol === null) {
- return null
- }
-
- const schema = url.protocol.slice(0, -1)
- const host = url.host
- const path = url.pathname
- return {
- title: 'Twitter',
- link: `https://twitter.com/intent/tweet?text=Wow:&url=${encodeURIComponent(
- url.href
- )}`,
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: { url: `${schema}://${host}${path}` },
- },
- preview: {
- style: 'social',
- },
- }
-}
-
-function githubIssues(user, repo) {
- const repoSlug = `${user}/${repo}`
- return {
- title: 'GitHub issues',
- link: `https://github.com/${repoSlug}/issues`,
- example: {
- pattern: '/github/issues/:user/:repo',
- namedParams: { user, repo },
- queryParams: {},
- },
- }
-}
-
-function githubForks(user, repo) {
- const repoSlug = `${user}/${repo}`
- return {
- title: 'GitHub forks',
- link: `https://github.com/${repoSlug}/network`,
- example: {
- pattern: '/github/forks/:user/:repo',
- namedParams: { user, repo },
- queryParams: {},
- },
- }
-}
-
-function githubStars(user, repo) {
- const repoSlug = `${user}/${repo}`
- return {
- title: 'GitHub stars',
- link: `https://github.com/${repoSlug}/stargazers`,
- example: {
- pattern: '/github/stars/:user/:repo',
- namedParams: { user, repo },
- queryParams: {},
- },
- }
-}
-
-async function githubLicense(githubApiProvider, user, repo) {
- const repoSlug = `${user}/${repo}`
-
- let link = `https://github.com/${repoSlug}`
-
- const { buffer } = await githubApiProvider.requestAsPromise(
- request,
- `/repos/${repoSlug}/license`
- )
- try {
- const data = JSON.parse(buffer)
- if ('html_url' in data) {
- link = data.html_url
- }
- } catch (e) {}
-
- return {
- title: 'GitHub license',
- link,
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user, repo },
- queryParams: {},
- },
- }
-}
-
-function gitlabPipeline(user, repo) {
- const repoSlug = `${user}/${repo}`
- return {
- title: 'GitLab pipeline',
- link: `https://gitlab.com/${repoSlug}/builds`,
- example: {
- pattern: '/gitlab/pipeline/:user/:repo',
- namedParams: { user, repo },
- queryParams: {},
- },
- }
-}
-
-async function findSuggestions(githubApiProvider, url) {
- let promises = []
- if (url.hostname === 'github.com' || url.hostname === 'gitlab.com') {
- const userRepo = url.pathname.slice(1).split('/')
- const user = userRepo[0]
- const repo = userRepo[1]
- if (url.hostname === 'github.com') {
- promises = promises.concat([
- githubIssues(user, repo),
- githubForks(user, repo),
- githubStars(user, repo),
- githubLicense(githubApiProvider, user, repo),
- ])
- } else {
- promises = promises.concat([gitlabPipeline(user, repo)])
- }
- }
- promises.push(twitterPage(url))
-
- const suggestions = await Promise.all(promises)
-
- return suggestions.filter(b => b != null)
-}
-
-// data: {url}, JSON-serializable object.
-// end: function(json), with json of the form:
-// - suggestions: list of objects of the form:
-// - title: string
-// - link: target as a string URL
-// - example: object
-// - pattern: string
-// - namedParams: object
-// - queryParams: object (optional)
-// - link: target as a string URL
-// - preview: object (optional)
-// - style: string
-function setRoutes(allowedOrigin, githubApiProvider, server) {
- server.ajax.on('suggest/v1', (data, end, ask) => {
- // The typical dev and production setups are cross-origin. However, in
- // Heroku deploys and some self-hosted deploys these requests may come from
- // the same host. Chrome does not send an Origin header on same-origin
- // requests, but Firefox does.
- //
- // It would be better to solve this problem using some well-tested
- // middleware.
- const origin = ask.req.headers.origin
- if (origin) {
- let host
- try {
- host = new URL(origin).hostname
- } catch (e) {
- ask.res.setHeader('Access-Control-Allow-Origin', 'null')
- end({ err: 'Disallowed' })
- return
- }
-
- if (host !== ask.req.headers.host) {
- if (allowedOrigin.includes(origin)) {
- ask.res.setHeader('Access-Control-Allow-Origin', origin)
- } else {
- ask.res.setHeader('Access-Control-Allow-Origin', 'null')
- end({ err: 'Disallowed' })
- return
- }
- }
- }
-
- let url
- try {
- url = new URL(data.url)
- } catch (e) {
- end({ err: `${e}` })
- return
- }
-
- findSuggestions(githubApiProvider, url)
- // This interacts with callback code and can't use async/await.
- // eslint-disable-next-line promise/prefer-await-to-then
- .then(suggestions => {
- end({ suggestions })
- })
- .catch(err => {
- end({ suggestions: [], err })
- })
- })
-}
-
-module.exports = {
- findSuggestions,
- githubLicense,
- setRoutes,
-}
diff --git a/services/suggest.spec.js b/services/suggest.spec.js
deleted file mode 100644
index eda2672ae5030..0000000000000
--- a/services/suggest.spec.js
+++ /dev/null
@@ -1,180 +0,0 @@
-'use strict'
-
-const Camp = require('@shields_io/camp')
-const { expect } = require('chai')
-const nock = require('nock')
-const portfinder = require('portfinder')
-const got = require('../core/got-test-client')
-const { setRoutes, githubLicense } = require('./suggest')
-const GithubApiProvider = require('./github/github-api-provider')
-
-describe('Badge suggestions', function() {
- const githubApiBaseUrl = 'https://api.github.test'
- const apiProvider = new GithubApiProvider({
- baseUrl: githubApiBaseUrl,
- globalToken: 'fake-token',
- withPooling: false,
- })
-
- describe('GitHub license', function() {
- context('When html_url included in response', function() {
- it('Should link to it', async function() {
- const scope = nock(githubApiBaseUrl)
- .get('/repos/atom/atom/license')
- .reply(200, {
- html_url: 'https://github.com/atom/atom/blob/master/LICENSE.md',
- license: {
- key: 'mit',
- name: 'MIT License',
- spdx_id: 'MIT',
- url: 'https://api.github.com/licenses/mit',
- node_id: 'MDc6TGljZW5zZTEz',
- },
- })
-
- expect(await githubLicense(apiProvider, 'atom', 'atom')).to.deep.equal({
- title: 'GitHub license',
- link: 'https://github.com/atom/atom/blob/master/LICENSE.md',
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- })
-
- scope.done()
- })
- })
-
- context('When html_url not included in response', function() {
- it('Should link to the repo', async function() {
- const scope = nock(githubApiBaseUrl)
- .get('/repos/atom/atom/license')
- .reply(200, {
- license: { key: 'mit' },
- })
-
- expect(await githubLicense(apiProvider, 'atom', 'atom')).to.deep.equal({
- title: 'GitHub license',
- link: 'https://github.com/atom/atom',
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- })
-
- scope.done()
- })
- })
- })
-
- describe('Scoutcamp integration', function() {
- let port, baseUrl
- before(async function() {
- port = await portfinder.getPortPromise()
- baseUrl = `http://127.0.0.1:${port}`
- })
-
- let camp
- before(async function() {
- camp = Camp.start({ port, hostname: '::' })
- await new Promise(resolve => camp.on('listening', () => resolve()))
- })
- after(async function() {
- if (camp) {
- await new Promise(resolve => camp.close(resolve))
- camp = undefined
- }
- })
-
- const origin = 'https://example.test'
- before(function() {
- setRoutes([origin], apiProvider, camp)
- })
-
- context('without an origin header', function() {
- it('returns the expected suggestions', async function() {
- const scope = nock(githubApiBaseUrl)
- .get('/repos/atom/atom/license')
- .reply(200, {
- html_url: 'https://github.com/atom/atom/blob/master/LICENSE.md',
- license: {
- key: 'mit',
- name: 'MIT License',
- spdx_id: 'MIT',
- url: 'https://api.github.com/licenses/mit',
- node_id: 'MDc6TGljZW5zZTEz',
- },
- })
-
- const { statusCode, body } = await got(
- `${baseUrl}/$suggest/v1?url=${encodeURIComponent(
- 'https://github.com/atom/atom'
- )}`,
- {
- json: true,
- }
- )
- expect(statusCode).to.equal(200)
- expect(body).to.deep.equal({
- suggestions: [
- {
- title: 'GitHub issues',
- link: 'https://github.com/atom/atom/issues',
- example: {
- pattern: '/github/issues/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub forks',
- link: 'https://github.com/atom/atom/network',
- example: {
- pattern: '/github/forks/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub stars',
- link: 'https://github.com/atom/atom/stargazers',
- example: {
- pattern: '/github/stars/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'GitHub license',
- link: 'https://github.com/atom/atom/blob/master/LICENSE.md',
- example: {
- pattern: '/github/license/:user/:repo',
- namedParams: { user: 'atom', repo: 'atom' },
- queryParams: {},
- },
- },
- {
- title: 'Twitter',
- link:
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fatom%2Fatom',
- example: {
- pattern: '/twitter/url',
- namedParams: {},
- queryParams: {
- url: 'https://github.com/atom/atom',
- },
- },
- preview: {
- style: 'social',
- },
- },
- ],
- })
-
- scope.done()
- })
- })
- })
-})
diff --git a/services/swagger/swagger-redirect.service.js b/services/swagger/swagger-redirect.service.js
index 954bd131b0365..489f39a4c07bb 100644
--- a/services/swagger/swagger-redirect.service.js
+++ b/services/swagger/swagger-redirect.service.js
@@ -1,20 +1,15 @@
-'use strict'
+import { retiredService } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
- redirector({
+export default [
+ retiredService({
category: 'other',
+ label: 'swagger',
name: 'SwaggerRedirect',
route: {
base: 'swagger/valid/2.0',
pattern: ':scheme(http|https)/:url*',
},
- transformPath: () => `/swagger/valid/3.0`,
- transformQueryParams: ({ scheme, url }) => {
- const suffix = /(yaml|yml|json)$/.test(url) ? '' : '.json'
- return { specUrl: `${scheme}://${url}${suffix}` }
- },
- dateAdded: new Date('2019-11-03'),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
]
diff --git a/services/swagger/swagger-redirect.tester.js b/services/swagger/swagger-redirect.tester.js
index 3fec29b7d6a9d..f135928490bec 100644
--- a/services/swagger/swagger-redirect.tester.js
+++ b/services/swagger/swagger-redirect.tester.js
@@ -1,33 +1,22 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'SwaggerUrlRedirect',
title: 'SwaggerUrlRedirect',
pathPrefix: '/swagger/valid/2.0',
-}))
+})
-t.create('swagger json')
- .get('/https/example.com/example.svg')
- .expectRedirect(
- `/swagger/valid/3.0.svg?specUrl=${encodeURIComponent(
- 'https://example.com/example.json'
- )}`
- )
+t.create('swagger json').get('/https/example.com/example.json').expectBadge({
+ label: 'swagger',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
-t.create('swagger yml')
- .get('/https/example.com/example.yml')
- .expectRedirect(
- `/swagger/valid/3.0.svg?specUrl=${encodeURIComponent(
- 'https://example.com/example.yml'
- )}`
- )
+t.create('swagger yml').get('/https/example.com/example.json').expectBadge({
+ label: 'swagger',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
-t.create('swagger yaml')
- .get('/https/example.com/example.yaml')
- .expectRedirect(
- `/swagger/valid/3.0.svg?specUrl=${encodeURIComponent(
- 'https://example.com/example.yaml'
- )}`
- )
+t.create('swagger yaml').get('/https/example.com/example.json').expectBadge({
+ label: 'swagger',
+ message: 'https://github.com/badges/shields/pull/11583',
+})
diff --git a/services/swagger/swagger.service.js b/services/swagger/swagger.service.js
index 636ae1b80d9fb..87bc1e70d725e 100644
--- a/services/swagger/swagger.service.js
+++ b/services/swagger/swagger.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseJsonService, NotFound, queryParams } from '../index.js'
const schema = Joi.object()
.keys({
@@ -10,44 +8,40 @@ const schema = Joi.object()
Joi.object({
level: Joi.string().required(),
message: Joi.string().required(),
- }).required()
+ }),
),
})
.required()
const queryParamSchema = Joi.object({
- specUrl: optionalUrl.required(),
+ specUrl: url,
}).required()
-module.exports = class SwaggerValidatorService extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class SwaggerValidatorService extends BaseJsonService {
+ static category = 'other'
- static get route() {
- return {
- base: 'swagger/valid',
- pattern: '3.0',
- queryParamSchema,
- }
+ static route = {
+ base: 'swagger/valid',
+ pattern: '3.0',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Swagger Validator',
- staticPreview: this.render({ status: 'valid' }),
- namedParams: {},
- queryParams: {
- specUrl:
- 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore-expanded.json',
- },
+ static openApi = {
+ '/swagger/valid/3.0': {
+ get: {
+ summary: 'Swagger Validator',
+ parameters: queryParams({
+ name: 'specUrl',
+ required: true,
+ example:
+ 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/c442afe06ec28443df0c69d01dc38c54968b246f/examples/v2.0/json/petstore-expanded.json',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'swagger' }
+ static defaultBadgeData = {
+ label: 'swagger',
}
static render({ status }) {
@@ -60,10 +54,10 @@ module.exports = class SwaggerValidatorService extends BaseJsonService {
async fetch({ specUrl }) {
return this._requestJson({
- url: 'http://validator.swagger.io/validator/debug',
+ url: 'https://validator.swagger.io/validator/debug',
schema,
options: {
- qs: {
+ searchParams: {
url: specUrl,
},
},
@@ -77,7 +71,7 @@ module.exports = class SwaggerValidatorService extends BaseJsonService {
} else if (valMessages.length === 1) {
const { message, level } = valMessages[0]
if (level === 'error' && message === `Can't read from file ${specUrl}`) {
- throw new NotFound({ prettyMessage: 'spec not found or unreadable ' })
+ throw new NotFound({ prettyMessage: 'spec not found or unreadable' })
}
}
if (valMessages.every(msg => msg.level === 'warning')) {
diff --git a/services/swagger/swagger.tester.js b/services/swagger/swagger.tester.js
index 3f8749e46ff4e..3f61f55306851 100644
--- a/services/swagger/swagger.tester.js
+++ b/services/swagger/swagger.tester.js
@@ -1,14 +1,13 @@
-'use strict'
+import { createServiceTester } from '../tester.js'
const getURL = '/3.0.json?specUrl=https://example.com/example.json'
const getURLBase = '/3.0.json?specUrl='
-const apiURL = 'http://validator.swagger.io'
+const apiURL = 'https://validator.swagger.io'
const apiGetURL = '/validator/debug'
const apiGetQueryParams = {
url: 'https://example.com/example.json',
}
-
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = await createServiceTester()
t.create('Invalid')
.get(getURL)
@@ -23,7 +22,7 @@ t.create('Invalid')
message: 'error',
},
],
- })
+ }),
)
.expectBadge({
label: 'swagger',
@@ -33,7 +32,7 @@ t.create('Invalid')
t.create('Valid json 2.0')
.get(
- `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore-expanded.json`
+ `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/c442afe06ec28443df0c69d01dc38c54968b246f/examples/v2.0/json/petstore-expanded.json`,
)
.expectBadge({
label: 'swagger',
@@ -43,7 +42,7 @@ t.create('Valid json 2.0')
t.create('Valid yaml 3.0')
.get(
- `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml`
+ `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/c442afe06ec28443df0c69d01dc38c54968b246f/examples/v3.0/petstore.yaml`,
)
.expectBadge({
label: 'swagger',
@@ -62,7 +61,7 @@ t.create('Valid with warnings')
// Isn't a spec, but valid json
t.create('Invalid')
.get(
- `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json`
+ `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/c442afe06ec28443df0c69d01dc38c54968b246f/schemas/v3.0/schema.json`,
)
.expectBadge({
label: 'swagger',
@@ -72,7 +71,7 @@ t.create('Invalid')
t.create('Not found')
.get(
- `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/notFound.yaml`
+ `${getURLBase}https://raw.githubusercontent.com/OAI/OpenAPI-Specification/c442afe06ec28443df0c69d01dc38c54968b246f/examples/v3.0/notFound.yaml`,
)
.expectBadge({
label: 'swagger',
diff --git a/services/symfony/sensiolabs-redirect.service.js b/services/symfony/sensiolabs-redirect.service.js
index 35cdae90c4f13..5196e02b8d4a7 100644
--- a/services/symfony/sensiolabs-redirect.service.js
+++ b/services/symfony/sensiolabs-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
// The SymfonyInsight service was previously branded as SensioLabs, and
// accordingly the badge path used to be /sensiolabs/i/projectUuid'.
redirector({
diff --git a/services/symfony/sensiolabs-redirect.tester.js b/services/symfony/sensiolabs-redirect.tester.js
index 274ddd8387134..d6e60ceafb1f4 100644
--- a/services/symfony/sensiolabs-redirect.tester.js
+++ b/services/symfony/sensiolabs-redirect.tester.js
@@ -1,12 +1,10 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'sensiolabs',
title: 'SensioLabs',
-}))
+})
t.create('sensiolabs insight')
- .get('/i/45afb680-d4e6-4e66-93ea-bcfa79eb8a87.svg')
- .expectRedirect('/symfony/i/grade/45afb680-d4e6-4e66-93ea-bcfa79eb8a87.svg')
+ .get('/i/a92cacf2-ba32-4f36-b040-5a9f1d7f8f25.svg')
+ .expectRedirect('/symfony/i/grade/a92cacf2-ba32-4f36-b040-5a9f1d7f8f25.svg')
diff --git a/services/symfony/symfony-insight-base.js b/services/symfony/symfony-insight-base.js
index e521536216e2c..3bd1a3cbc0d27 100644
--- a/services/symfony/symfony-insight-base.js
+++ b/services/symfony/symfony-insight-base.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseXmlService, NotFound } = require('..')
+import Joi from 'joi'
+import { BaseXmlService, NotFound } from '../index.js'
const violationSchema = Joi.object({
severity: Joi.equal('info', 'minor', 'major', 'critical').required(),
@@ -15,27 +13,29 @@ const schema = Joi.object({
'running',
'measured',
'analyzed',
- 'finished'
+ 'finished',
)
.allow('')
.required(),
grade: Joi.equal('platinum', 'gold', 'silver', 'bronze', 'none'),
- violations: Joi.object({
- // RE: https://github.com/NaturalIntelligence/fast-xml-parser/issues/68
- // The BaseXmlService uses the fast-xml-parser which doesn't support forcing
- // the xml nodes to always be parsed as an array. Currently, if the response
- // only contains a single violation then it will be parsed as an object,
- // otherwise it will be parsed as an array.
- violation: Joi.array()
- .items(violationSchema)
- .single()
- .required(),
- }),
+ violations: Joi.alternatives().try(
+ Joi.object({
+ // RE: https://github.com/NaturalIntelligence/fast-xml-parser/issues/68
+ // The BaseXmlService uses the fast-xml-parser which doesn't support forcing
+ // the xml nodes to always be parsed as an array. Currently, if the response
+ // only contains a single violation then it will be parsed as an object,
+ // otherwise it will be parsed as an array.
+ violation: Joi.array().items(violationSchema).single().required(),
+ }),
+ // If no violations are found, the response will be an empty string.
+ Joi.string().allow(''),
+ ),
}),
}).required(),
}).required()
-const keywords = ['sensiolabs', 'sensio']
+const description =
+ 'SymfonyInsight (formerly SensioLabs) is a code analysis service'
const gradeColors = {
none: 'red',
@@ -46,23 +46,17 @@ const gradeColors = {
}
class SymfonyInsightBase extends BaseXmlService {
- static get category() {
- return 'analysis'
- }
+ static category = 'analysis'
- static get auth() {
- return {
- userKey: 'sl_insight_userUuid',
- passKey: 'sl_insight_apiToken',
- authorizedOrigins: ['https://insight.symfony.com'],
- isRequired: true,
- }
+ static auth = {
+ userKey: 'sl_insight_userUuid',
+ passKey: 'sl_insight_apiToken',
+ authorizedOrigins: ['https://insight.symfony.com'],
+ isRequired: true,
}
- static get defaultBadgeData() {
- return {
- label: 'symfony insight',
- }
+ static defaultBadgeData = {
+ label: 'symfony insight',
}
async fetch({ projectUuid }) {
@@ -73,7 +67,7 @@ class SymfonyInsightBase extends BaseXmlService {
options: {
headers: { Accept: 'application/vnd.com.sensiolabs.insight+xml' },
},
- errorMessages: {
+ httpErrors: {
401: 'not authorized to access project',
404: 'project not found',
},
@@ -81,7 +75,7 @@ class SymfonyInsightBase extends BaseXmlService {
attributeNamePrefix: '',
ignoreAttributes: false,
},
- })
+ }),
)
}
@@ -135,8 +129,4 @@ class SymfonyInsightBase extends BaseXmlService {
}
}
-module.exports = {
- SymfonyInsightBase,
- keywords,
- gradeColors,
-}
+export { SymfonyInsightBase, description, gradeColors }
diff --git a/services/symfony/symfony-insight-base.spec.js b/services/symfony/symfony-insight-base.spec.js
index d7d4af6b4fd2c..91af55d63539b 100644
--- a/services/symfony/symfony-insight-base.spec.js
+++ b/services/symfony/symfony-insight-base.spec.js
@@ -1,12 +1,10 @@
-'use strict'
+import { expect } from 'chai'
+import { NotFound } from '../index.js'
+import { SymfonyInsightBase } from './symfony-insight-base.js'
-const { expect } = require('chai')
-const { NotFound } = require('..')
-const { SymfonyInsightBase } = require('./symfony-insight-base')
-
-describe('SymfonyInsightBase', function() {
- context('transform()', function() {
- it('throws NotFound error when there is no coverage data', function() {
+describe('SymfonyInsightBase', function () {
+ context('transform()', function () {
+ it('throws NotFound error when there is no coverage data', function () {
try {
SymfonyInsightBase.prototype.transform({
data: { project: {} },
diff --git a/services/symfony/symfony-insight-grade.service.js b/services/symfony/symfony-insight-grade.service.js
index ac5ae0a91ff78..9af12ec56c7d3 100644
--- a/services/symfony/symfony-insight-grade.service.js
+++ b/services/symfony/symfony-insight-grade.service.js
@@ -1,33 +1,27 @@
-'use strict'
-
-const {
+import { pathParams } from '../index.js'
+import {
SymfonyInsightBase,
- keywords,
+ description,
gradeColors,
-} = require('./symfony-insight-base')
+} from './symfony-insight-base.js'
-module.exports = class SymfonyInsightGrade extends SymfonyInsightBase {
- static get route() {
- return {
- base: 'symfony/i/grade',
- pattern: ':projectUuid',
- }
+export default class SymfonyInsightGrade extends SymfonyInsightBase {
+ static route = {
+ base: 'symfony/i/grade',
+ pattern: ':projectUuid',
}
- static get examples() {
- return [
- {
- title: 'SymfonyInsight Grade',
- namedParams: {
- projectUuid: '45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
- },
- staticPreview: this.render({
- grade: 'bronze',
- status: 'finished',
+ static openApi = {
+ '/symfony/i/grade/{projectUuid}': {
+ get: {
+ summary: 'SymfonyInsight Grade',
+ description,
+ parameters: pathParams({
+ name: 'projectUuid',
+ example: 'a92cacf2-ba32-4f36-b040-5a9f1d7f8f25',
}),
- keywords,
},
- ]
+ },
}
static render({ status, grade = 'none' }) {
diff --git a/services/symfony/symfony-insight-grade.spec.js b/services/symfony/symfony-insight-grade.spec.js
new file mode 100644
index 0000000000000..28bd368c062a6
--- /dev/null
+++ b/services/symfony/symfony-insight-grade.spec.js
@@ -0,0 +1,17 @@
+import { testAuth } from '../test-helpers.js'
+import SymfonyInsightGrade from './symfony-insight-grade.service.js'
+
+describe('SymfonyInsightGrade', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SymfonyInsightGrade,
+ 'BasicAuth',
+ `
+ gold
+ finished
+ `,
+ )
+ })
+ })
+})
diff --git a/services/symfony/symfony-insight-grade.tester.js b/services/symfony/symfony-insight-grade.tester.js
index 6b53c2d2a891b..8b69c22633dbe 100644
--- a/services/symfony/symfony-insight-grade.tester.js
+++ b/services/symfony/symfony-insight-grade.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
-const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers')
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { sampleProjectUuid, noSymfonyToken } from './symfony-test-helpers.js'
+export const t = await createServiceTester()
t.create('valid project grade')
.skipWhen(noSymfonyToken)
@@ -15,7 +14,7 @@ t.create('valid project grade')
'gold',
'silver',
'bronze',
- 'no medal'
+ 'no medal',
).required(),
})
diff --git a/services/symfony/symfony-insight-stars.service.js b/services/symfony/symfony-insight-stars.service.js
index 38470f455e748..ae2d005d3584e 100644
--- a/services/symfony/symfony-insight-stars.service.js
+++ b/services/symfony/symfony-insight-stars.service.js
@@ -1,11 +1,10 @@
-'use strict'
-
-const { starRating } = require('../text-formatters')
-const {
+import { pathParams } from '../index.js'
+import { starRating } from '../text-formatters.js'
+import {
SymfonyInsightBase,
- keywords,
+ description,
gradeColors,
-} = require('./symfony-insight-base')
+} from './symfony-insight-base.js'
const gradeStars = {
none: 0,
@@ -15,28 +14,23 @@ const gradeStars = {
platinum: 4,
}
-module.exports = class SymfonyInsightStars extends SymfonyInsightBase {
- static get route() {
- return {
- base: 'symfony/i/stars',
- pattern: ':projectUuid',
- }
+export default class SymfonyInsightStars extends SymfonyInsightBase {
+ static route = {
+ base: 'symfony/i/stars',
+ pattern: ':projectUuid',
}
- static get examples() {
- return [
- {
- title: 'SymfonyInsight Stars',
- namedParams: {
- projectUuid: '45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
- },
- staticPreview: this.render({
- grade: 'silver',
- status: 'finished',
+ static openApi = {
+ '/symfony/i/stars/{projectUuid}': {
+ get: {
+ summary: 'SymfonyInsight Stars',
+ description,
+ parameters: pathParams({
+ name: 'projectUuid',
+ example: 'a92cacf2-ba32-4f36-b040-5a9f1d7f8f25',
}),
- keywords,
},
- ]
+ },
}
static render({ status, grade }) {
diff --git a/services/symfony/symfony-insight-stars.spec.js b/services/symfony/symfony-insight-stars.spec.js
new file mode 100644
index 0000000000000..e1a3deffb7502
--- /dev/null
+++ b/services/symfony/symfony-insight-stars.spec.js
@@ -0,0 +1,17 @@
+import { testAuth } from '../test-helpers.js'
+import SymfonyInsightStars from './symfony-insight-stars.service.js'
+
+describe('SymfonyInsightStars', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SymfonyInsightStars,
+ 'BasicAuth',
+ `
+ gold
+ finished
+ `,
+ )
+ })
+ })
+})
diff --git a/services/symfony/symfony-insight-stars.tester.js b/services/symfony/symfony-insight-stars.tester.js
index 7bc4162ae7968..0500bf49871e6 100644
--- a/services/symfony/symfony-insight-stars.tester.js
+++ b/services/symfony/symfony-insight-stars.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { withRegex } = require('../test-validators')
-const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers')
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+import { sampleProjectUuid, noSymfonyToken } from './symfony-test-helpers.js'
+export const t = await createServiceTester()
t.create('valid project stars')
.skipWhen(noSymfonyToken)
@@ -11,7 +10,7 @@ t.create('valid project stars')
.expectBadge({
label: 'stars',
message: withRegex(
- /^(?=.{4}$)(\u2605{0,4}[\u00BC\u00BD\u00BE]?\u2606{0,4})$/
+ /^(?=.{4}$)(\u2605{0,4}[\u00BC\u00BD\u00BE]?\u2606{0,4})$/,
),
})
diff --git a/services/symfony/symfony-insight-violations.service.js b/services/symfony/symfony-insight-violations.service.js
index 270506502794b..250bffaf2d8e6 100644
--- a/services/symfony/symfony-insight-violations.service.js
+++ b/services/symfony/symfony-insight-violations.service.js
@@ -1,29 +1,23 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { SymfonyInsightBase, description } from './symfony-insight-base.js'
-const { SymfonyInsightBase, keywords } = require('./symfony-insight-base')
-
-module.exports = class SymfonyInsightViolations extends SymfonyInsightBase {
- static get route() {
- return {
- base: 'symfony/i/violations',
- pattern: ':projectUuid',
- }
+export default class SymfonyInsightViolations extends SymfonyInsightBase {
+ static route = {
+ base: 'symfony/i/violations',
+ pattern: ':projectUuid',
}
- static get examples() {
- return [
- {
- title: 'SymfonyInsight Violations',
- namedParams: {
- projectUuid: '45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
- },
- staticPreview: this.render({
- numViolations: 0,
- status: 'finished',
+ static openApi = {
+ '/symfony/i/violations/{projectUuid}': {
+ get: {
+ summary: 'SymfonyInsight Violations',
+ description,
+ parameters: pathParams({
+ name: 'projectUuid',
+ example: 'a92cacf2-ba32-4f36-b040-5a9f1d7f8f25',
}),
- keywords,
},
- ]
+ },
}
static render({
diff --git a/services/symfony/symfony-insight-violations.spec.js b/services/symfony/symfony-insight-violations.spec.js
new file mode 100644
index 0000000000000..0c20a3838a785
--- /dev/null
+++ b/services/symfony/symfony-insight-violations.spec.js
@@ -0,0 +1,16 @@
+import { testAuth } from '../test-helpers.js'
+import SymfonyInsightViolations from './symfony-insight-violations.service.js'
+
+describe('SymfonyInsightViolations', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ SymfonyInsightViolations,
+ 'BasicAuth',
+ `
+ finished
+ `,
+ )
+ })
+ })
+})
diff --git a/services/symfony/symfony-insight-violations.tester.js b/services/symfony/symfony-insight-violations.tester.js
index 5a053a44bf1ce..c0b4795188e95 100644
--- a/services/symfony/symfony-insight-violations.tester.js
+++ b/services/symfony/symfony-insight-violations.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { withRegex } = require('../test-validators')
-const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers')
+import { createServiceTester } from '../tester.js'
+import { withRegex } from '../test-validators.js'
+import { sampleProjectUuid, noSymfonyToken } from './symfony-test-helpers.js'
+export const t = await createServiceTester()
t.create('valid project violations')
.skipWhen(noSymfonyToken)
@@ -11,6 +10,6 @@ t.create('valid project violations')
.expectBadge({
label: 'violations',
message: withRegex(
- /\d* critical|\d* critical, \d* major|\d* critical, \d* major, \d* minor|\d* critical, \d* major, \d* minor, \d* info|\d* critical, \d* minor|\d* critical, \d* info|\d* major|\d* major, \d* minor|\d* major, \d* minor, \d* info|\d* major, \d* info|\d* minor|\d* minor, \d* info/
+ /0|\d* critical|\d* critical, \d* major|\d* critical, \d* major, \d* minor|\d* critical, \d* major, \d* minor, \d* info|\d* critical, \d* minor|\d* critical, \d* info|\d* major|\d* major, \d* minor|\d* major, \d* minor, \d* info|\d* major, \d* info|\d* minor|\d* minor, \d* info|\d* info/,
),
})
diff --git a/services/symfony/symfony-insight.spec.js b/services/symfony/symfony-insight.spec.js
index acc48a3e7133a..46ddb28e49846 100644
--- a/services/symfony/symfony-insight.spec.js
+++ b/services/symfony/symfony-insight.spec.js
@@ -1,13 +1,11 @@
-'use strict'
-
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const SymfonyInsightGrade = require('./symfony-insight-grade.service')
-const SymfonyInsightStars = require('./symfony-insight-stars.service')
-const SymfonyInsightViolations = require('./symfony-insight-violations.service')
-const {
- sampleProjectUuid: projectUuid,
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import SymfonyInsightGrade from './symfony-insight-grade.service.js'
+import SymfonyInsightStars from './symfony-insight-stars.service.js'
+import SymfonyInsightViolations from './symfony-insight-violations.service.js'
+import {
+ sampleProjectUuid as projectUuid,
runningMockResponse,
platinumMockResponse,
goldMockResponse,
@@ -23,7 +21,7 @@ const {
user,
token,
config,
-} = require('./symfony-test-helpers')
+} from './symfony-test-helpers.js'
// These tests are organized in a fairly unusual way because the service uses
// XML, so it's difficult to decouple the parsing from the transform + render.
@@ -32,7 +30,7 @@ const {
//
// In most other cases, do not follow this pattern. Instead, write a .spec file
// with sazerac tests of the transform and render functions.
-describe('SymfonyInsight[Grade|Stars|Violation]', function() {
+describe('SymfonyInsight[Grade|Stars|Violation]', function () {
cleanUpNockAfterEach()
function createMock() {
@@ -41,10 +39,10 @@ describe('SymfonyInsight[Grade|Stars|Violation]', function() {
.basicAuth({ user, pass: token })
}
- it('401 not authorized grade', async function() {
+ it('401 not authorized grade', async function () {
const scope = createMock().reply(401)
expect(
- await SymfonyInsightGrade.invoke(defaultContext, config, { projectUuid })
+ await SymfonyInsightGrade.invoke(defaultContext, config, { projectUuid }),
).to.deep.equal({
message: 'not authorized to access project',
color: 'lightgray',
@@ -65,38 +63,38 @@ describe('SymfonyInsight[Grade|Stars|Violation]', function() {
throw Error(`Oops, what are those doing there: ${rest.join(', ')}`)
}
- describe(description, function() {
+ describe(description, function () {
if (expectedGradeBadge) {
- it('grade', async function() {
+ it('grade', async function () {
const scope = createMock().reply(200, response)
expect(
await SymfonyInsightGrade.invoke(defaultContext, config, {
projectUuid,
- })
+ }),
).to.deep.equal(expectedGradeBadge)
scope.done()
})
}
if (expectedStarsBadge) {
- it('stars', async function() {
+ it('stars', async function () {
const scope = createMock().reply(200, response)
expect(
await SymfonyInsightStars.invoke(defaultContext, config, {
projectUuid,
- })
+ }),
).to.deep.equal(expectedStarsBadge)
scope.done()
})
}
if (expectedViolationsBadge) {
- it('violations', async function() {
+ it('violations', async function () {
const scope = createMock().reply(200, response)
expect(
await SymfonyInsightViolations.invoke(defaultContext, config, {
projectUuid,
- })
+ }),
).to.deep.equal(expectedViolationsBadge)
scope.done()
})
diff --git a/services/symfony/symfony-test-helpers.js b/services/symfony/symfony-test-helpers.js
index f5d544beef1c3..ea99fa14d4baf 100644
--- a/services/symfony/symfony-test-helpers.js
+++ b/services/symfony/symfony-test-helpers.js
@@ -1,6 +1,7 @@
-'use strict'
+import { noToken } from '../test-helpers.js'
+import { SymfonyInsightBase } from './symfony-insight-base.js'
-const sampleProjectUuid = '45afb680-d4e6-4e66-93ea-bcfa79eb8a87'
+const sampleProjectUuid = 'a92cacf2-ba32-4f36-b040-5a9f1d7f8f25'
function createMockResponse({ status = 'finished', grade, violations }) {
let response = `
@@ -85,12 +86,9 @@ const config = {
sl_insight_apiToken: token,
},
}
-
-const { noToken } = require('../test-helpers')
-const { SymfonyInsightBase } = require('./symfony-insight-base')
const noSymfonyToken = noToken(SymfonyInsightBase)
-module.exports = {
+export {
sampleProjectUuid,
runningMockResponse,
platinumMockResponse,
diff --git a/services/teamcity/teamcity-base.js b/services/teamcity/teamcity-base.js
index e280235d20da8..d8abbf48c0051 100644
--- a/services/teamcity/teamcity-base.js
+++ b/services/teamcity/teamcity-base.js
@@ -1,21 +1,17 @@
-'use strict'
+import { BaseJsonService } from '../index.js'
-const { BaseJsonService } = require('..')
-
-module.exports = class TeamCityBase extends BaseJsonService {
- static get auth() {
- return {
- userKey: 'teamcity_user',
- passKey: 'teamcity_pass',
- serviceKey: 'teamcity',
- }
+export default class TeamCityBase extends BaseJsonService {
+ static auth = {
+ userKey: 'teamcity_user',
+ passKey: 'teamcity_pass',
+ serviceKey: 'teamcity',
}
- async fetch({ url, schema, qs = {}, errorMessages = {} }) {
+ async fetch({ url, schema, searchParams = {}, httpErrors = {} }) {
// JetBrains API Auth Docs: https://confluence.jetbrains.com/display/TCD18/REST+API#RESTAPI-RESTAuthentication
- const options = { qs }
+ const options = { searchParams }
if (!this.authHelper.isConfigured) {
- qs.guest = 1
+ searchParams.guest = 1
}
return this._requestJson(
@@ -23,8 +19,8 @@ module.exports = class TeamCityBase extends BaseJsonService {
url,
schema,
options,
- errorMessages: { 404: 'build not found', ...errorMessages },
- })
+ httpErrors: { 404: 'build not found', ...httpErrors },
+ }),
)
}
}
diff --git a/services/teamcity/teamcity-build-redirect.service.js b/services/teamcity/teamcity-build-redirect.service.js
index 09684097b3ba9..a9ac6b026994c 100644
--- a/services/teamcity/teamcity-build-redirect.service.js
+++ b/services/teamcity/teamcity-build-redirect.service.js
@@ -1,28 +1,20 @@
-'use strict'
+import { retiredService, redirector } from '../index.js'
-const { redirector } = require('..')
-
-const commonAttrs = {
- dateAdded: new Date('2019-09-15'),
- category: 'build',
-}
-
-module.exports = [
- redirector({
- ...commonAttrs,
+export default [
+ retiredService({
name: 'TeamCityBuildLegacyCodeBetterRedirect',
+ category: 'build',
+ label: 'teamcity',
route: {
base: 'teamcity/codebetter',
pattern: ':buildId',
},
- transformPath: ({ buildId }) => `/teamcity/build/s/${buildId}`,
- transformQueryParams: _params => ({
- server: 'https://teamcity.jetbrains.com',
- }),
+ dateAdded: new Date('2025-12-20'),
+ issueUrl: 'https://github.com/badges/shields/pull/11583',
}),
redirector({
- ...commonAttrs,
name: 'TeamCityBuildRedirect',
+ category: 'build',
route: {
base: 'teamcity',
pattern:
@@ -33,5 +25,6 @@ module.exports = [
transformQueryParams: ({ protocol, hostAndPath }) => ({
server: `${protocol}://${hostAndPath}`,
}),
+ dateAdded: new Date('2019-09-15'),
}),
]
diff --git a/services/teamcity/teamcity-build-redirect.tester.js b/services/teamcity/teamcity-build-redirect.tester.js
index fc1870445c79b..37eab2d3e3e62 100644
--- a/services/teamcity/teamcity-build-redirect.tester.js
+++ b/services/teamcity/teamcity-build-redirect.tester.js
@@ -1,33 +1,30 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'TeamCityBuildRedirect',
title: 'TeamCityBuildRedirect',
pathPrefix: '/teamcity',
-}))
+})
t.create('codebetter')
- .get('/codebetter/IntelliJIdeaCe_JavaDecompilerEngineTests.svg')
- .expectRedirect(
- `/teamcity/build/s/IntelliJIdeaCe_JavaDecompilerEngineTests.svg?server=${encodeURIComponent(
- 'https://teamcity.jetbrains.com'
- )}`
- )
+ .get('/codebetter/IntelliJIdeaCe_JavaDecompilerEngineTests.json')
+ .expectBadge({
+ label: 'teamcity',
+ message: 'https://github.com/badges/shields/pull/11583',
+ })
t.create('hostAndPath simple build')
.get('/https/teamcity.jetbrains.com/s/bt345.svg')
.expectRedirect(
`/teamcity/build/s/bt345.svg?server=${encodeURIComponent(
- 'https://teamcity.jetbrains.com'
- )}`
+ 'https://teamcity.jetbrains.com',
+ )}`,
)
t.create('hostAndPath full build')
.get('/https/teamcity.jetbrains.com/e/bt345.svg')
.expectRedirect(
`/teamcity/build/e/bt345.svg?server=${encodeURIComponent(
- 'https://teamcity.jetbrains.com'
- )}`
+ 'https://teamcity.jetbrains.com',
+ )}`,
)
diff --git a/services/teamcity/teamcity-build.service.js b/services/teamcity/teamcity-build.service.js
index eba17ddd04ba9..3eaa918968610 100644
--- a/services/teamcity/teamcity-build.service.js
+++ b/services/teamcity/teamcity-build.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const TeamCityBase = require('./teamcity-base')
+import Joi from 'joi'
+import { optionalUrl } from '../validators.js'
+import { pathParam, queryParam } from '../index.js'
+import TeamCityBase from './teamcity-base.js'
const buildStatusSchema = Joi.object({
status: Joi.equal('SUCCESS', 'FAILURE', 'ERROR').required(),
@@ -13,57 +12,47 @@ const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class TeamCityBuild extends TeamCityBase {
- static get category() {
- return 'build'
- }
+export default class TeamCityBuild extends TeamCityBase {
+ static category = 'build'
- static get route() {
- return {
- base: 'teamcity/build',
- pattern: ':verbosity(s|e)/:buildId',
- queryParamSchema,
- }
+ static route = {
+ base: 'teamcity/build',
+ pattern: ':verbosity(s|e)/:buildId',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'TeamCity Simple Build Status',
- namedParams: {
- verbosity: 's',
- buildId: 'IntelliJIdeaCe_JavaDecompilerEngineTests',
- },
- queryParams: {
- server: 'https://teamcity.jetbrains.com',
- },
- staticPreview: this.render({
- status: 'SUCCESS',
- }),
+ static openApi = {
+ '/teamcity/build/s/{buildId}': {
+ get: {
+ summary: 'TeamCity Simple Build Status',
+ parameters: [
+ pathParam({
+ name: 'buildId',
+ example: 'IntelliJIdeaCe_JavaDecompilerEngineTests',
+ }),
+ queryParam({
+ name: 'server',
+ example: 'https://teamcity.jetbrains.com',
+ }),
+ ],
},
- {
- title: 'TeamCity Full Build Status',
- namedParams: {
- verbosity: 'e',
- buildId: 'bt345',
- },
- queryParams: {
- server: 'https://teamcity.jetbrains.com',
- },
- staticPreview: this.render({
- status: 'FAILURE',
- statusText: 'Tests failed: 4, passed: 1103, ignored: 2',
- useVerbose: true,
- }),
- keywords: ['test', 'test results'],
+ },
+ '/teamcity/build/e/{buildId}': {
+ get: {
+ summary: 'TeamCity Full Build Status',
+ parameters: [
+ pathParam({ name: 'buildId', example: 'bt345' }),
+ queryParam({
+ name: 'server',
+ example: 'https://teamcity.jetbrains.com',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
+ static defaultBadgeData = {
+ label: 'build',
}
static render({ status, statusText, useVerbose }) {
@@ -87,7 +76,7 @@ module.exports = class TeamCityBuild extends TeamCityBase {
async handle(
{ verbosity, buildId },
- { server = 'https://teamcity.jetbrains.com' }
+ { server = 'https://teamcity.jetbrains.com' },
) {
// JetBrains Docs: https://confluence.jetbrains.com/display/TCD18/REST+API#RESTAPI-BuildStatusIcon
const buildLocator = `buildType:(id:${buildId})`
diff --git a/services/teamcity/teamcity-build.spec.js b/services/teamcity/teamcity-build.spec.js
index 06aee3668f8db..91ba4c6a85253 100644
--- a/services/teamcity/teamcity-build.spec.js
+++ b/services/teamcity/teamcity-build.spec.js
@@ -1,41 +1,16 @@
-'use strict'
+import { testAuth } from '../test-helpers.js'
+import TeamCityBuild from './teamcity-build.service.js'
+import { config } from './teamcity-test-helpers.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const TeamCityBuild = require('./teamcity-build.service')
-const { user, pass, host, config } = require('./teamcity-test-helpers')
-
-describe('TeamCityBuild', function() {
- cleanUpNockAfterEach()
-
- it('sends the auth information as configured', async function() {
- const scope = nock(`https://${host}`)
- .get(`/app/rest/builds/${encodeURIComponent('buildType:(id:bt678)')}`)
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, {
- status: 'FAILURE',
- statusText:
- 'Tests failed: 1 (1 new), passed: 50246, ignored: 1, muted: 12',
- })
-
- expect(
- await TeamCityBuild.invoke(
- defaultContext,
- config,
- {
- verbosity: 'e',
- buildId: 'bt678',
- },
- { server: `https://${host}` }
+describe('TeamCityBuild', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ TeamCityBuild,
+ 'BasicAuth',
+ { status: 'SUCCESS', statusText: 'Success' },
+ { configOverride: config },
)
- ).to.deep.equal({
- message: 'tests failed: 1 (1 new), passed: 50246, ignored: 1, muted: 12',
- color: 'red',
})
-
- scope.done()
})
})
diff --git a/services/teamcity/teamcity-build.tester.js b/services/teamcity/teamcity-build.tester.js
index 8ca8f74d5f475..85900fd401063 100644
--- a/services/teamcity/teamcity-build.tester.js
+++ b/services/teamcity/teamcity-build.tester.js
@@ -1,11 +1,11 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { withRegex } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const buildStatusValues = Joi.equal('passing', 'failure', 'error').required()
-const buildStatusTextRegex = /^success|failure|error|tests( failed: \d+( \(\d+ new\))?)?(,)?( passed: \d+)?(,)?( ignored: \d+)?(,)?( muted: \d+)?/
+const buildStatusTextRegex =
+ /^success|failure|error|tests( failed: \d+( \(\d+ new\))?)?(,)?( passed: \d+)?(,)?( ignored: \d+)?(,)?( muted: \d+)?/
t.create('unknown build')
.get('/s/btabc.json?server=https://teamcity.jetbrains.com')
@@ -34,7 +34,7 @@ t.create('codebetter success build')
.reply(200, {
status: 'SUCCESS',
statusText: 'Success',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -51,7 +51,7 @@ t.create('codebetter failure build')
.reply(200, {
status: 'FAILURE',
statusText: 'Tests failed: 2',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -68,7 +68,7 @@ t.create('simple build status with passed build')
.reply(200, {
status: 'SUCCESS',
statusText: 'Tests passed: 100',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -85,7 +85,7 @@ t.create('simple build status with failed build')
.reply(200, {
status: 'FAILURE',
statusText: 'Tests failed: 10 (2 new)',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -102,7 +102,7 @@ t.create('full build status with passed build')
.reply(200, {
status: 'SUCCESS',
statusText: 'Tests passed: 100, ignored: 3',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -119,7 +119,7 @@ t.create('full build status with failed build')
.reply(200, {
status: 'FAILURE',
statusText: 'Tests failed: 10 (2 new), passed: 99',
- })
+ }),
)
.expectBadge({
label: 'build',
@@ -136,7 +136,7 @@ t.create('full build status with passed build chain')
.reply(200, {
status: 'SUCCESS',
statusText: 'Build chain finished (success: 9)',
- })
+ }),
)
.expectBadge({
label: 'build',
diff --git a/services/teamcity/teamcity-coverage-redirect.service.js b/services/teamcity/teamcity-coverage-redirect.service.js
deleted file mode 100644
index d41c37dabc312..0000000000000
--- a/services/teamcity/teamcity-coverage-redirect.service.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-module.exports = [
- redirector({
- category: 'coverage',
- route: {
- base: 'teamcity/coverage',
- pattern: ':protocol(http|https)/:hostAndPath(.+)/:buildId',
- },
- transformPath: ({ buildId }) => `/teamcity/coverage/${buildId}`,
- transformQueryParams: ({ protocol, hostAndPath }) => ({
- server: `${protocol}://${hostAndPath}`,
- }),
- dateAdded: new Date('2019-09-15'),
- }),
-]
diff --git a/services/teamcity/teamcity-coverage-redirect.tester.js b/services/teamcity/teamcity-coverage-redirect.tester.js
deleted file mode 100644
index df0decb3912b7..0000000000000
--- a/services/teamcity/teamcity-coverage-redirect.tester.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'TeamCityCoverageRedirect',
- title: 'TeamCityCoverageRedirect',
- pathPrefix: '/teamcity/coverage',
-}))
-
-t.create('coverage')
- .get('/https/teamcity.jetbrains.com/ReactJSNet_PullRequests.svg')
- .expectRedirect(
- `/teamcity/coverage/ReactJSNet_PullRequests.svg?server=${encodeURIComponent(
- 'https://teamcity.jetbrains.com'
- )}`
- )
diff --git a/services/teamcity/teamcity-coverage.service.js b/services/teamcity/teamcity-coverage.service.js
index 3d088b4a01950..726b49e61069a 100644
--- a/services/teamcity/teamcity-coverage.service.js
+++ b/services/teamcity/teamcity-coverage.service.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { coveragePercentage } = require('../color-formatters')
-const { optionalUrl } = require('../validators')
-const { InvalidResponse } = require('..')
-const TeamCityBase = require('./teamcity-base')
+import Joi from 'joi'
+import { coveragePercentage } from '../color-formatters.js'
+import { optionalUrl } from '../validators.js'
+import { InvalidResponse, pathParam, queryParam } from '../index.js'
+import TeamCityBase from './teamcity-base.js'
const buildStatisticsSchema = Joi.object({
property: Joi.array()
@@ -12,7 +10,7 @@ const buildStatisticsSchema = Joi.object({
Joi.object({
name: Joi.string().required(),
value: Joi.string().required(),
- })
+ }),
)
.required(),
}).required()
@@ -21,40 +19,32 @@ const queryParamSchema = Joi.object({
server: optionalUrl,
}).required()
-module.exports = class TeamCityCoverage extends TeamCityBase {
- static get category() {
- return 'coverage'
- }
+export default class TeamCityCoverage extends TeamCityBase {
+ static category = 'coverage'
- static get route() {
- return {
- base: 'teamcity/coverage',
- pattern: ':buildId',
- queryParamSchema,
- }
+ static route = {
+ base: 'teamcity/coverage',
+ pattern: ':buildId',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'TeamCity Coverage',
- namedParams: {
- buildId: 'ReactJSNet_PullRequests',
- },
- queryParams: {
- server: 'https://teamcity.jetbrains.com',
- },
- staticPreview: this.render({
- coverage: 82,
- }),
+ static openApi = {
+ '/teamcity/coverage/{buildId}': {
+ get: {
+ summary: 'TeamCity Coverage',
+ parameters: [
+ pathParam({ name: 'buildId', example: 'FileHelpersStable' }),
+ queryParam({
+ name: 'server',
+ example: 'https://teamcity.jetbrains.com',
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'coverage',
- }
+ static defaultBadgeData = {
+ label: 'coverage',
}
static render({ coverage }) {
@@ -87,7 +77,7 @@ module.exports = class TeamCityCoverage extends TeamCityBase {
// JetBrains Docs: https://confluence.jetbrains.com/display/TCD18/REST+API#RESTAPI-Statistics
const buildLocator = `buildType:(id:${buildId})`
const apiPath = `app/rest/builds/${encodeURIComponent(
- buildLocator
+ buildLocator,
)}/statistics`
const data = await this.fetch({
url: `${server}/${apiPath}`,
diff --git a/services/teamcity/teamcity-coverage.spec.js b/services/teamcity/teamcity-coverage.spec.js
index c193caede9c09..7bec5ef4c774e 100644
--- a/services/teamcity/teamcity-coverage.spec.js
+++ b/services/teamcity/teamcity-coverage.spec.js
@@ -1,46 +1,21 @@
-'use strict'
+import { testAuth } from '../test-helpers.js'
+import TeamCityCoverage from './teamcity-coverage.service.js'
+import { config } from './teamcity-test-helpers.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const TeamCityCoverage = require('./teamcity-coverage.service')
-const { user, pass, host, config } = require('./teamcity-test-helpers')
-
-describe('TeamCityCoverage', function() {
- cleanUpNockAfterEach()
-
- it('sends the auth information as configured', async function() {
- const scope = nock(`https://${host}`)
- .get(
- `/app/rest/builds/${encodeURIComponent(
- 'buildType:(id:bt678)'
- )}/statistics`
- )
- .query({})
- // This ensures that the expected credentials are actually being sent with the HTTP request.
- // Without this the request wouldn't match and the test would fail.
- .basicAuth({ user, pass })
- .reply(200, {
- property: [
- { name: 'CodeCoverageAbsSCovered', value: '82' },
- { name: 'CodeCoverageAbsSTotal', value: '100' },
- ],
- })
-
- expect(
- await TeamCityCoverage.invoke(
- defaultContext,
- config,
+describe('TeamCityCoverage', function () {
+ describe('auth', function () {
+ it('sends the auth information as configured', async function () {
+ return testAuth(
+ TeamCityCoverage,
+ 'BasicAuth',
{
- buildId: 'bt678',
+ property: [
+ { name: 'CodeCoverageAbsSCovered', value: '93' },
+ { name: 'CodeCoverageAbsSTotal', value: '95' },
+ ],
},
- { server: 'https://mycompany.teamcity.com' }
+ { configOverride: config },
)
- ).to.deep.equal({
- message: '82%',
- color: 'yellowgreen',
})
-
- scope.done()
})
})
diff --git a/services/teamcity/teamcity-coverage.tester.js b/services/teamcity/teamcity-coverage.tester.js
index 9be283712febf..a6515d243d87e 100644
--- a/services/teamcity/teamcity-coverage.tester.js
+++ b/services/teamcity/teamcity-coverage.tester.js
@@ -1,21 +1,17 @@
-'use strict'
-
-const { isIntegerPercentage } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isIntegerPercentage } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('invalid buildId')
.get('/btABC999.json')
.expectBadge({ label: 'coverage', message: 'build not found' })
t.create('valid buildId')
- .get('/ReactJSNet_PullRequests.json')
- .expectBadge({
- label: 'coverage',
- message: isIntegerPercentage,
- })
+ .get('/FileHelpersStable.json')
+ .expectBadge({ label: 'coverage', message: isIntegerPercentage })
t.create('specified instance valid buildId')
- .get('/ReactJSNet_PullRequests.json?server=https://teamcity.jetbrains.com')
+ .get('/FileHelpersStable.json?server=https://teamcity.jetbrains.com')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
@@ -27,7 +23,7 @@ t.create('no coverage data for build')
nock('https://teamcity.jetbrains.com/app/rest/builds')
.get(`/${encodeURIComponent('buildType:(id:bt234)')}/statistics`)
.query({ guest: 1 })
- .reply(200, { property: [] })
+ .reply(200, { property: [] }),
)
.expectBadge({ label: 'coverage', message: 'no coverage data available' })
@@ -48,7 +44,7 @@ t.create('zero lines covered')
value: '345',
},
],
- })
+ }),
)
.expectBadge({
label: 'coverage',
diff --git a/services/teamcity/teamcity-test-helpers.js b/services/teamcity/teamcity-test-helpers.js
index 21d7a8b3ad89c..a3051df624a82 100644
--- a/services/teamcity/teamcity-test-helpers.js
+++ b/services/teamcity/teamcity-test-helpers.js
@@ -1,25 +1,13 @@
-'use strict'
+import TeamCityBase from './teamcity-base.js'
-const user = 'admin'
-const pass = 'password'
-const host = 'mycompany.teamcity.com'
const config = {
public: {
services: {
- teamcity: {
- authorizedOrigins: [`https://${host}`],
+ [TeamCityBase.auth.serviceKey]: {
+ authorizedOrigins: ['https://teamcity.jetbrains.com'],
},
},
},
- private: {
- teamcity_user: user,
- teamcity_pass: pass,
- },
}
-module.exports = {
- user,
- pass,
- host,
- config,
-}
+export { config }
diff --git a/services/terraform/terraform-base.js b/services/terraform/terraform-base.js
new file mode 100644
index 0000000000000..b5c2a61d0b277
--- /dev/null
+++ b/services/terraform/terraform-base.js
@@ -0,0 +1,50 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
+
+const description =
+ '[Terraform Registry](https://registry.terraform.io) is an interactive resource for discovering a wide selection of integrations (providers), configuration packages (modules), and security rules (policies) for use with Terraform.'
+
+const schema = Joi.object({
+ data: Joi.object({
+ attributes: Joi.object({
+ month: nonNegativeInteger,
+ total: nonNegativeInteger,
+ week: nonNegativeInteger,
+ year: nonNegativeInteger,
+ }).required(),
+ }),
+})
+
+const intervalMap = {
+ dw: {
+ transform: json => json.data.attributes.week,
+ interval: 'week',
+ },
+ dm: {
+ transform: json => json.data.attributes.month,
+ interval: 'month',
+ },
+ dy: {
+ transform: json => json.data.attributes.year,
+ interval: 'year',
+ },
+ dt: {
+ transform: json => json.data.attributes.total,
+ interval: '',
+ },
+}
+
+class BaseTerraformService extends BaseJsonService {
+ static _cacheLength = 3600
+
+ async fetch({ kind, object }) {
+ const url = `https://registry.terraform.io/v2/${kind}/${object}/downloads/summary`
+ return this._requestJson({
+ schema,
+ url,
+ })
+ }
+}
+
+export { BaseTerraformService, intervalMap, description }
diff --git a/services/terraform/terraform-module-downloads.service.js b/services/terraform/terraform-module-downloads.service.js
new file mode 100644
index 0000000000000..7f944c3ce9b50
--- /dev/null
+++ b/services/terraform/terraform-module-downloads.service.js
@@ -0,0 +1,60 @@
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import {
+ BaseTerraformService,
+ description,
+ intervalMap,
+} from './terraform-base.js'
+
+export default class TerraformModuleDownloads extends BaseTerraformService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'terraform/module',
+ pattern: ':interval(dw|dm|dy|dt)/:namespace/:name/:provider',
+ }
+
+ static openApi = {
+ '/terraform/module/{interval}/{namespace}/{name}/{provider}': {
+ get: {
+ summary: 'Terraform Module Downloads',
+ description,
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dy',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Weekly, Monthly, Yearly or Total downloads',
+ },
+ {
+ name: 'namespace',
+ example: 'hashicorp',
+ },
+ {
+ name: 'name',
+ example: 'consul',
+ },
+ {
+ name: 'provider',
+ example: 'aws',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ interval, namespace, name, provider }) {
+ const { transform } = intervalMap[interval]
+ const json = await this.fetch({
+ kind: 'modules',
+ object: `${namespace}/${name}/${provider}`,
+ })
+
+ return renderDownloadsBadge({
+ downloads: transform(json),
+ interval: intervalMap[interval].interval,
+ })
+ }
+}
diff --git a/services/terraform/terraform-module-downloads.tester.js b/services/terraform/terraform-module-downloads.tester.js
new file mode 100644
index 0000000000000..990e9509fd9e7
--- /dev/null
+++ b/services/terraform/terraform-module-downloads.tester.js
@@ -0,0 +1,36 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('weekly downloads (valid)')
+ .get('/dw/hashicorp/consul/aws.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('monthly downloads (valid)')
+ .get('/dm/hashicorp/consul/aws.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/dy/hashicorp/consul/aws.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('total downloads (valid)')
+ .get('/dt/hashicorp/consul/aws.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('weekly downloads (not found)')
+ .get('/dw/not/real/module.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('monthly downloads (not found)')
+ .get('/dm/not/real/module.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/dy/not/real/module.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('total downloads (not found)')
+ .get('/dt/not/real/module.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
diff --git a/services/terraform/terraform-provider-downloads.service.js b/services/terraform/terraform-provider-downloads.service.js
new file mode 100644
index 0000000000000..a0d7225c09b38
--- /dev/null
+++ b/services/terraform/terraform-provider-downloads.service.js
@@ -0,0 +1,54 @@
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import {
+ BaseTerraformService,
+ description,
+ intervalMap,
+} from './terraform-base.js'
+
+export default class TerraformProviderDownloads extends BaseTerraformService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'terraform/provider',
+ pattern: ':interval(dw|dm|dy|dt)/:providerId',
+ }
+
+ static openApi = {
+ '/terraform/provider/{interval}/{providerId}': {
+ get: {
+ summary: 'Terraform Provider Downloads',
+ description,
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dy',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Weekly, Monthly, Yearly or Total downloads',
+ },
+ {
+ name: 'providerId',
+ example: '323',
+ description:
+ 'The provider ID can be found using `https://registry.terraform.io/v2/providers/{namespace}/{name}`',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'downloads' }
+
+ async handle({ interval, providerId }) {
+ const { transform } = intervalMap[interval]
+ const json = await this.fetch({
+ kind: 'providers',
+ object: providerId,
+ })
+
+ return renderDownloadsBadge({
+ downloads: transform(json),
+ interval: intervalMap[interval].interval,
+ })
+ }
+}
diff --git a/services/terraform/terraform-provider-downloads.tester.js b/services/terraform/terraform-provider-downloads.tester.js
new file mode 100644
index 0000000000000..83c3c2147f681
--- /dev/null
+++ b/services/terraform/terraform-provider-downloads.tester.js
@@ -0,0 +1,36 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('weekly downloads (valid)')
+ .get('/dw/323.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('monthly downloads (valid)')
+ .get('/dm/323.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('yearly downloads (valid)')
+ .get('/dy/323.json')
+ .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod })
+
+t.create('total downloads (valid)')
+ .get('/dt/323.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('weekly downloads (not found)')
+ .get('/dw/not-valid.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('monthly downloads (not found)')
+ .get('/dm/not-valid.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('yearly downloads (not found)')
+ .get('/dy/not-valid.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
+
+t.create('total downloads (not found)')
+ .get('/dt/not-valid.json')
+ .expectBadge({ label: 'downloads', message: 'not found' })
diff --git a/services/test-helpers.js b/services/test-helpers.js
index aa8edee4f6c81..02173f6bf0feb 100644
--- a/services/test-helpers.js
+++ b/services/test-helpers.js
@@ -1,12 +1,14 @@
-'use strict'
-
-const nock = require('nock')
-const request = require('request')
-const runnerConfig = require('config').util.toObject()
-const { promisify } = require('../core/base-service/legacy-request-handler')
+import _ from 'lodash'
+import dayjs from 'dayjs'
+import { expect } from 'chai'
+import nock from 'nock'
+import config from 'config'
+import { fetch } from '../core/base-service/got.js'
+import BaseService from '../core/base-service/base.js'
+const runnerConfig = config.util.toObject()
function cleanUpNockAfterEach() {
- afterEach(function() {
+ afterEach(function () {
nock.restore()
nock.cleanAll()
nock.enableNetConnect()
@@ -24,7 +26,7 @@ function noToken(serviceClass) {
(passKey && !runnerConfig.private[passKey])
if (noToken && !hasLogged) {
console.warn(
- `${serviceClass.name}: no credentials configured, tests for this service will be skipped. Add credentials in local.yml to run them.`
+ `${serviceClass.name}: no credentials configured, tests for this service will be skipped. Add credentials in local.yml to run them.`,
)
hasLogged = true
}
@@ -32,13 +34,407 @@ function noToken(serviceClass) {
}
}
-const sendAndCacheRequest = promisify(request)
+/**
+ * Retrieves an example set of parameters for invoking a service class using OpenAPI example of that class.
+ *
+ * @param {BaseService} serviceClass The service class containing OpenAPI specifications.
+ * @param {'path'|'query'} paramType The type of params to extract, may be path params or query params.
+ * @returns {object} An object with call params to use with a service invoke of the first OpenAPI example.
+ * @throws {TypeError} - Throws a TypeError if the input `serviceClass` is not an instance of BaseService,
+ * or if it lacks the expected structure.
+ *
+ * @example
+ * // Example usage:
+ * const example = getBadgeExampleCall(StackExchangeReputation)
+ * console.log(example)
+ * // Output: { stackexchangesite: 'stackoverflow', query: '123' }
+ * StackExchangeReputation.invoke(defaultContext, config, example)
+ */
+function getBadgeExampleCall(serviceClass, paramType) {
+ if (!(serviceClass.prototype instanceof BaseService)) {
+ throw new TypeError(
+ 'Invalid serviceClass: Must be an instance of BaseService.',
+ )
+ }
+
+ if (Object.keys(serviceClass.openApi).length === 0) {
+ console.warn(
+ `Missing OpenAPI in service class ${serviceClass.name}. Make sure to use exampleOverride in testAuth.`,
+ )
+ return {}
+ }
+ if (!['path', 'query'].includes(paramType)) {
+ throw new TypeError('Invalid paramType: Must be path or query.')
+ }
+
+ const firstOpenapiPath = Object.keys(serviceClass.openApi)[0]
+
+ const firstOpenapiExampleParams =
+ serviceClass.openApi[firstOpenapiPath].get.parameters
+ if (!Array.isArray(firstOpenapiExampleParams)) {
+ throw new TypeError(
+ `Missing or invalid OpenAPI examples in ${serviceClass.name}.`,
+ )
+ }
+
+ // reformat structure for serviceClass.invoke
+ const exampleInvokeParams = firstOpenapiExampleParams.reduce((acc, obj) => {
+ if (obj.in === paramType) {
+ let example = obj.example
+ if (obj?.schema?.type === 'boolean') {
+ example = example || ''
+ }
+ acc[obj.name] = example
+ }
+ return acc
+ }, {})
+
+ return exampleInvokeParams
+}
+
+/**
+ * Generates a configuration object with a fake key based on the provided class.
+ * For use in auth tests where a config with a test key is required.
+ *
+ * @param {BaseService} serviceClass - The class to generate configuration for.
+ * @param {string} fakeKey - The fake key to be used in the configuration.
+ * @param {string} fakeUser - Optional, The fake user to be used in the configuration.
+ * @param {string[]} fakeauthorizedOrigins - authorizedOrigins to add to config.
+ * @param {object} authOverride Return result with overrid params.
+ * @returns {object} - The configuration object.
+ * @throws {TypeError} - Throws an error if the input is not a class.
+ */
+function generateFakeConfig(
+ serviceClass,
+ fakeKey,
+ fakeUser,
+ fakeauthorizedOrigins,
+ authOverride,
+) {
+ if (
+ !serviceClass ||
+ !serviceClass.prototype ||
+ !(serviceClass.prototype instanceof BaseService)
+ ) {
+ throw new TypeError(
+ 'Invalid serviceClass: Must be an instance of BaseService.',
+ )
+ }
+ if (!fakeKey && !fakeUser) {
+ throw new TypeError('Must provide at least one: fakeKey or fakeUser.')
+ }
+ if (!fakeauthorizedOrigins || !Array.isArray(fakeauthorizedOrigins)) {
+ throw new TypeError('Invalid fakeauthorizedOrigins: Must be an array.')
+ }
+
+ const auth = { ...serviceClass.auth, ...authOverride }
+ if (Object.keys(auth).length === 0) {
+ throw new Error(`Auth empty for ${serviceClass.name}.`)
+ }
+ if (fakeKey && typeof fakeKey !== 'string') {
+ throw new Error('Invalid fakeKey: Must be a String.')
+ }
+ if (fakeKey && !auth.passKey) {
+ throw new Error(`Missing auth.passKey for ${serviceClass.name}.`)
+ }
+ // Extract the passKey property from auth, or use a default if not present
+ const passKeyProperty = auth.passKey ? auth.passKey : undefined
+ if (fakeUser && typeof fakeUser !== 'string') {
+ throw new TypeError('Invalid fakeUser: Must be a String.')
+ }
+ if (fakeUser && !auth.userKey) {
+ throw new Error(`Missing auth.userKey for ${serviceClass.name}.`)
+ }
+ const passUserProperty = auth.userKey ? auth.userKey : undefined
+
+ // Build and return the configuration object with the fake key
+ return {
+ public: {
+ services: {
+ [auth.serviceKey]: {
+ authorizedOrigins: fakeauthorizedOrigins,
+ },
+ },
+ },
+ private: {
+ [passKeyProperty]: fakeKey,
+ [passUserProperty]: fakeUser,
+ },
+ }
+}
+
+/**
+ * Returns the first auth origin found for a provided service class.
+ *
+ * @param {BaseService} serviceClass The service class to find the authorized origins.
+ * @param {object} authOverride Return result with overridden params.
+ * @param {object} configOverride - Override the config.
+ * @throws {TypeError} - Throws a TypeError if the input `serviceClass` is not an instance of BaseService.
+ * @returns {string[]} First auth origin found.
+ *
+ * @example
+ * // Example usage:
+ * getServiceClassAuthOrigin(Obs)
+ * // outputs ['https://api.opensuse.org']
+ */
+function getServiceClassAuthOrigin(serviceClass, authOverride, configOverride) {
+ if (
+ !serviceClass ||
+ !serviceClass.prototype ||
+ !(serviceClass.prototype instanceof BaseService)
+ ) {
+ throw new TypeError(
+ `Invalid serviceClass ${serviceClass}: Must be an instance of BaseService.`,
+ )
+ }
+ const auth = { ...serviceClass.auth, ...authOverride }
+ if (auth.authorizedOrigins) {
+ return auth.authorizedOrigins
+ } else {
+ const mergedConfig = _.merge(runnerConfig, configOverride)
+ if (!mergedConfig.public.services[auth.serviceKey]) {
+ throw new TypeError(
+ `Missing service key definition for ${auth.serviceKey}: Use an override if applicable.`,
+ )
+ }
+ return [mergedConfig.public.services[auth.serviceKey].authorizedOrigins]
+ }
+}
+
+/**
+ * Generate a fake JWT Token valid for 1 hour for use in testing.
+ *
+ * @returns {string} Fake JWT Token valid for 1 hour.
+ */
+function fakeJwtToken() {
+ const fakeJwtPayload = { exp: dayjs().add(1, 'hours').unix() }
+ const fakeJwtPayloadJsonString = JSON.stringify(fakeJwtPayload)
+ const fakeJwtPayloadBase64 = Buffer.from(fakeJwtPayloadJsonString).toString(
+ 'base64',
+ )
+ const jwtToken = `FakeHeader.${fakeJwtPayloadBase64}.fakeSignature`
+ return jwtToken
+}
+
+/**
+ * Test authentication of a badge for it's first OpenAPI example using a provided dummyResponse and authentication method.
+ *
+ * @param {BaseService} serviceClass The service class tested.
+ * @param {'BasicAuth'|'ApiKeyHeader'|'BearerAuthHeader'|'QueryStringAuth'|'JwtAuth'} authMethod The auth method of the tested service class.
+ * @param {object} dummyResponse An object containing the dummy response by the server.
+ * @param {object} options - Additional options for non default keys and content-type of the dummy response.
+ * @param {string} options.apiHeaderKey - Non default header for ApiKeyHeader auth.
+ * @param {string} options.bearerHeaderKey - Non default bearer header prefix for BearerAuthHeader.
+ * @param {string} options.queryUserKey - QueryStringAuth user key.
+ * @param {string} options.queryPassKey - QueryStringAuth pass key.
+ * @param {string} options.jwtLoginEndpoint - jwtAuth Login endpoint.
+ * @param {object} options.exampleOverride - Override example params in test.
+ * @param {object} options.authOverride - Override class auth params.
+ * @param {object} options.configOverride - Override the config for this test.
+ * @param {boolean} options.multipleRequests - For classes that require multiple requests to complete the test.
+ * @throws {TypeError} - Throws a TypeError if the input `serviceClass` is not an instance of BaseService,
+ * or if `serviceClass` is missing authorizedOrigins.
+ *
+ * @example
+ * // Example usage:
+ * testAuth(StackExchangeReputation, QueryStringAuth, { items: [{ reputation: 8 }] })
+ */
+async function testAuth(serviceClass, authMethod, dummyResponse, options = {}) {
+ if (!(serviceClass.prototype instanceof BaseService)) {
+ throw new TypeError(
+ 'Invalid serviceClass: Must be an instance of BaseService.',
+ )
+ }
+
+ const {
+ apiHeaderKey = 'x-api-key',
+ bearerHeaderKey = 'Bearer',
+ queryUserKey,
+ queryPassKey,
+ jwtLoginEndpoint,
+ exampleOverride = {},
+ authOverride,
+ configOverride,
+ multipleRequests = false,
+ } = options
+ const header = serviceClass.headers
+ ? { 'Content-Type': serviceClass.headers.Accept.split(', ')[0] }
+ : undefined
+ if (!apiHeaderKey || typeof apiHeaderKey !== 'string') {
+ throw new TypeError('Invalid apiHeaderKey: Must be a String.')
+ }
+ if (!bearerHeaderKey || typeof bearerHeaderKey !== 'string') {
+ throw new TypeError('Invalid bearerHeaderKey: Must be a String.')
+ }
+ if (typeof exampleOverride !== 'object') {
+ throw new TypeError('Invalid exampleOverride: Must be an Object.')
+ }
+ if (authOverride && typeof authOverride !== 'object') {
+ throw new TypeError('Invalid authOverride: Must be an Object.')
+ }
+ if (configOverride && typeof configOverride !== 'object') {
+ throw new TypeError('Invalid configOverride: Must be an Object.')
+ }
+ if (multipleRequests && typeof multipleRequests !== 'boolean') {
+ throw new TypeError('Invalid multipleRequests: Must be a boolean.')
+ }
+
+ if (!multipleRequests) {
+ cleanUpNockAfterEach()
+ }
+
+ const auth = { ...serviceClass.auth, ...authOverride }
+ const fakeUser = auth.userKey
+ ? 'fake-user'
+ : auth.defaultToEmptyStringForUser
+ ? ''
+ : undefined
+ const fakeSecret = auth.passKey ? 'fake-secret' : undefined
+ if (!fakeUser && !fakeSecret) {
+ throw new TypeError(
+ `Missing auth pass/user for ${serviceClass.name}. At least one is required.`,
+ )
+ }
+ const authOrigins = getServiceClassAuthOrigin(
+ serviceClass,
+ authOverride,
+ configOverride,
+ )
+ const config = generateFakeConfig(
+ serviceClass,
+ fakeSecret,
+ fakeUser,
+ authOrigins,
+ authOverride,
+ )
+ const exampleInvokePathParams = getBadgeExampleCall(serviceClass, 'path')
+ const exampleInvokeQueryParams = getBadgeExampleCall(serviceClass, 'query')
+ if (options && typeof options !== 'object') {
+ throw new TypeError('Invalid options: Must be an object.')
+ }
+
+ if (!authOrigins) {
+ throw new TypeError(`Missing authorizedOrigins for ${serviceClass.name}.`)
+ }
+ const jwtToken = authMethod === 'JwtAuth' ? fakeJwtToken() : undefined
+
+ const scopeArr = []
+ authOrigins.forEach(authOrigin => {
+ const scope = nock(authOrigin)
+ if (multipleRequests) {
+ scope.persist()
+ }
+ scopeArr.push(scope)
+ switch (authMethod) {
+ case 'BasicAuth':
+ scope
+ .get(/.*/)
+ .basicAuth({ user: fakeUser, pass: fakeSecret })
+ .reply(200, dummyResponse, header)
+ break
+ case 'ApiKeyHeader':
+ scope
+ .get(/.*/)
+ .matchHeader(apiHeaderKey, fakeSecret)
+ .reply(200, dummyResponse, header)
+ break
+ case 'BearerAuthHeader':
+ scope
+ .get(/.*/)
+ .matchHeader('Authorization', `${bearerHeaderKey} ${fakeSecret}`)
+ .reply(200, dummyResponse, header)
+ break
+ case 'QueryStringAuth':
+ if (!queryPassKey || typeof queryPassKey !== 'string') {
+ throw new TypeError('Invalid queryPassKey: Must be a String.')
+ }
+ scope
+ .get(/.*/)
+ .query(queryObject => {
+ if (queryObject[queryPassKey] !== fakeSecret) {
+ return false
+ }
+ if (queryUserKey) {
+ if (typeof queryUserKey !== 'string') {
+ throw new TypeError('Invalid queryUserKey: Must be a String.')
+ }
+ if (queryObject[queryUserKey] !== fakeUser) {
+ return false
+ }
+ }
+ return true
+ })
+ .reply(200, dummyResponse, header)
+ break
+ case 'JwtAuth': {
+ if (!jwtLoginEndpoint || typeof jwtLoginEndpoint !== 'string') {
+ throw new TypeError('Invalid jwtLoginEndpoint: Must be a String.')
+ }
+ if (jwtLoginEndpoint.startsWith(authOrigin)) {
+ scope
+ .post(/.*/, body => {
+ if (typeof body === 'object') {
+ return (
+ body.username === fakeUser && body.password === fakeSecret
+ )
+ }
+ if (typeof body === 'string') {
+ return (
+ body.includes(`username=${encodeURIComponent(fakeUser)}`) &&
+ body.includes(`password=${encodeURIComponent(fakeSecret)}`)
+ )
+ }
+ return false
+ })
+ .reply(200, { token: jwtToken })
+ } else {
+ scope
+ .get(/.*/)
+ .matchHeader('Authorization', `Bearer ${jwtToken}`)
+ .reply(200, dummyResponse, header)
+ }
+ break
+ }
+
+ default:
+ throw new TypeError(`Unkown auth method for ${serviceClass.name}.`)
+ }
+ })
+
+ expect(
+ await serviceClass.invoke(
+ defaultContext,
+ _.merge(config, configOverride),
+ {
+ ...exampleInvokePathParams,
+ ...exampleOverride,
+ },
+ {
+ ...exampleInvokeQueryParams,
+ ...exampleOverride,
+ },
+ ),
+ ).to.not.have.property('isError')
+
+ // clean up persistance if we have multiple requests
+ if (multipleRequests) {
+ scopeArr.forEach(scope => scope.persist(false))
+ nock.restore()
+ nock.cleanAll()
+ nock.enableNetConnect()
+ nock.activate()
+ }
+
+ // if we get 'Mocks not yet satisfied' we have redundent authOrigins or we are missing a critical request
+ scopeArr.forEach(scope => scope.done())
+}
-const defaultContext = { sendAndCacheRequest }
+const defaultContext = { requestFetcher: fetch }
-module.exports = {
+export {
cleanUpNockAfterEach,
noToken,
- sendAndCacheRequest,
+ getBadgeExampleCall,
+ testAuth,
defaultContext,
}
diff --git a/services/test-results.js b/services/test-results.js
index eaa139f11d790..9943c844db27b 100644
--- a/services/test-results.js
+++ b/services/test-results.js
@@ -1,6 +1,11 @@
-'use strict'
+/**
+ * Helpers for formatting test-result badge messages and colors.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
+import { queryParams } from './index.js'
const testResultQueryParamSchema = Joi.object({
compact_message: Joi.equal(''),
@@ -9,6 +14,31 @@ const testResultQueryParamSchema = Joi.object({
skipped_label: Joi.string(),
}).required()
+const testResultOpenApiQueryParams = queryParams(
+ {
+ name: 'compact_message',
+ example: null,
+ schema: { type: 'boolean' },
+ },
+ { name: 'passed_label', example: 'good' },
+ { name: 'failed_label', example: 'bad' },
+ { name: 'skipped_label', example: 'n/a' },
+)
+
+/**
+ * Format test counts into a badge message string.
+ *
+ * @param {object} attrs Refer to individual attrs
+ * @param {number} attrs.passed Number of passing tests.
+ * @param {number} attrs.failed Number of failing tests.
+ * @param {number} attrs.skipped Number of skipped tests.
+ * @param {number} attrs.total Total number of tests.
+ * @param {string} [attrs.passedLabel] Label for passing tests in verbose mode.
+ * @param {string} [attrs.failedLabel] Label for failing tests in verbose mode.
+ * @param {string} [attrs.skippedLabel] Label for skipped tests in verbose mode.
+ * @param {boolean} [attrs.isCompact] When true, use compact symbols separated by ` | `.
+ * @returns {string} Formatted message, or `no tests` when total is zero.
+ */
function renderTestResultMessage({
passed,
failed,
@@ -47,6 +77,23 @@ function renderTestResultMessage({
}
}
+/**
+ * Render badge data for a test result summary.
+ *
+ * Chooses badge color from pass/fail/skip counts and delegates message formatting
+ * to {@link renderTestResultMessage}.
+ *
+ * @param {object} attrs Refer to individual attrs
+ * @param {number} attrs.passed Number of passing tests.
+ * @param {number} attrs.failed Number of failing tests.
+ * @param {number} attrs.skipped Number of skipped tests.
+ * @param {number} attrs.total Total number of tests.
+ * @param {string} [attrs.passedLabel] Label for passing tests in verbose mode.
+ * @param {string} [attrs.failedLabel] Label for failing tests in verbose mode.
+ * @param {string} [attrs.skippedLabel] Label for skipped tests in verbose mode.
+ * @param {boolean} [attrs.isCompact] When true, use compact symbols in the message.
+ * @returns {{message: string, color: string}} Badge message and shields color name.
+ */
function renderTestResultBadge({
passed,
failed,
@@ -85,29 +132,22 @@ function renderTestResultBadge({
}
const documentation = `
-
- You may change the "passed", "failed" and "skipped" text on this badge by supplying query parameters &passed_label=, &failed_label= and &skipped_label= respectively.
-
+You may change the "passed", "failed" and "skipped" text on this badge by supplying query parameters &passed_label=, &failed_label= and &skipped_label= respectively.
+
+For example, if you want to use a different terminology:
+
+\`?passed_label=good&failed_label=bad&skipped_label=n%2Fa\`
-
- For example, if you want to use a different terminology:
-
- ?passed_label=good&failed_label=bad&skipped_label=n%2Fa
-
+Or symbols:
-
- Or symbols:
-
- ?compact_message&passed_label=💃&failed_label=🤦♀️&skipped_label=🤷
-
+\`?compact_message&passed_label=💃&failed_label=🤦♀️&skipped_label=🤷\`
-
- There is also a &compact_message query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
-
+There is also a &compact_message query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
`
-module.exports = {
+export {
testResultQueryParamSchema,
+ testResultOpenApiQueryParams,
renderTestResultMessage,
renderTestResultBadge,
documentation,
diff --git a/services/test-results.spec.js b/services/test-results.spec.js
index feb9a5771e8f3..d9c337676092c 100644
--- a/services/test-results.spec.js
+++ b/services/test-results.spec.js
@@ -1,12 +1,10 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const {
+import { test, given } from 'sazerac'
+import {
renderTestResultMessage,
renderTestResultBadge,
-} = require('./test-results')
+} from './test-results.js'
-describe('Test result helpers', function() {
+describe('Test result helpers', function () {
function renderBothStyles(props) {
const { message: standardMessage, color } = renderTestResultBadge(props)
const compactMessage = renderTestResultMessage({
diff --git a/services/test-validators.js b/services/test-validators.js
index 72e1378aba143..28f534c2763a6 100644
--- a/services/test-validators.js
+++ b/services/test-validators.js
@@ -1,142 +1,387 @@
-'use strict'
+/**
+ * Joi validators that are shared across more than one service's tests.
+ * Validators which are only used by one service should be declared in
+ * that service's test file.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
-const { semver: isSemver } = require('./validators')
-
-/*
- Note:
- Validators defined in this file are used by more than one service.
- Validators which are only used by one service
- should be declared in that service's test file.
-*/
+import Joi from 'joi'
+import { semver as isSemver } from './validators.js'
+/**
+ * Creates a Joi string validator that matches the given regular expression.
+ *
+ * @param {RegExp} re Regular expression the string must match
+ * @returns {Joi.StringSchema} Joi string schema validating against the regex
+ */
const withRegex = re => Joi.string().regex(re)
+/**
+ * Validates a version string with three dotted numeric clauses prefixed
+ * by `v`, e.g. `v1.2.3`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isVPlusTripleDottedVersion = withRegex(/^v[0-9]+.[0-9]+.[0-9]+$/)
+/**
+ * Validates a version string prefixed by `v` with at least a major version
+ * and optional minor and patch numbers, e.g. `v1`, `v1.2`, or `v1.2.3`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isVPlusDottedVersionAtLeastOne = withRegex(/^v\d+(\.\d+)?(\.\d+)?$/)
-// matches a version number with N 'clauses' e.g: v1.2 or v1.22.7.392 are valid
+/**
+ * Validates a version number prefixed by `v` with N 'clauses',
+ * e.g. `v1.2` or `v1.22.7.392`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isVPlusDottedVersionNClauses = withRegex(/^v\d+(\.\d+)*$/)
-// matches a version number with N 'clauses'
-// and an optional text suffix
-// e.g: -beta, -preview1, -release-candidate, +beta, ~pre9-12 etc
+/**
+ * Validates a version number prefixed by `v` with N 'clauses' and an
+ * optional text suffix, e.g. `-beta`, `-preview1`, `-release-candidate`,
+ * `+beta`, or `~pre9-12`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isVPlusDottedVersionNClausesWithOptionalSuffix = withRegex(
- /^v\d+(\.\d+)*([-+~].*)?$/
+ /^v\d+(\.\d+)*([-+~].*)?$/,
)
-// same as above, but also accepts an optional 'epoch' prefix that can be
-// found e.g. in distro package versions, like 4:6.3.0-4
+/**
+ * Same as {@link isVPlusDottedVersionNClausesWithOptionalSuffix}, but also
+ * accepts an optional 'epoch' prefix that can be found e.g. in distro
+ * package versions, like `4:6.3.0-4`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch = withRegex(
- /^v(\d+:)?\d+(\.\d+)*([-+~].*)?$/
-)
-
-// Simple regex for test Composer versions rule
-// https://getcomposer.org/doc/articles/versions.md
-// Examples:
-// 7.1
-// >=5.6
-// >1.0 <2.0
-// !=1.0 <1.1 || >=1.2
-// 7.1.*
-// 7.* || 5.6.*
-// This regex not support branches, minimum-stability, ref and any (*)
-// https://getcomposer.org/doc/04-schema.md#package-links
-// https://getcomposer.org/doc/04-schema.md#minimum-stability
+ /^v(\d+:)?\d+(\.\d+)*([-+~].*)?$/,
+)
+
+/**
+ * Simple regex for testing Composer version rules, e.g. `7.1`, `>=5.6`,
+ * `>1.0 <2.0`, `!=1.0 <1.1 || >=1.2`, `7.1.*`, or `7.* || 5.6.*`.
+ * This regex does not support branches, minimum-stability, ref, or any (`*`).
+ *
+ * @see https://getcomposer.org/doc/articles/versions.md
+ * @see https://getcomposer.org/doc/04-schema.md#package-links
+ * @see https://getcomposer.org/doc/04-schema.md#minimum-stability
+ * @type {Joi.StringSchema}
+ */
const isComposerVersion = withRegex(
- /^\s*(>=|>|<|<=|!=|\^|~)?\d+(\.(\*|(\d+(\.(\d+|\*))?)))?((\s*\|\|)?\s*(>=|>|<|<=|!=|\^|~)?\d+(\.(\*|(\d+(\.(\d+|\*))?)))?)*\s*$/
+ /^\*|(\s*(>=|>|<|<=|!=|\^|~)?\d+(\.(\*|(\d+(\.(\d+|\*))?)))?((\s*\|*)?\s*(>=|>|<|<=|!=|\^|~)?\d+(\.(\*|(\d+(\.(\d+|\*))?)))?)*\s*)$/,
)
-// Regex for validate php-version.versionReduction()
-// >= 7
-// >= 7.1
-// 5.4, 5.6, 7.2
-// 5.4 - 7.1, HHVM
+/**
+ * Validates the reduced PHP version string produced by
+ * `php-version.versionReduction()`, e.g. `>= 7`, `>= 7.1`,
+ * `5.4, 5.6, 7.2`, or `5.4 - 7.1, HHVM`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isPhpVersionReduction = withRegex(
- /^((>= \d+(\.\d+)?)|(\d+\.\d+(, \d+\.\d+)*)|(\d+\.\d+ - \d+\.\d+))(, HHVM)?$/
+ /^((>= \d+(\.\d+)?)|(\d+\.\d+(, \d+\.\d+)*)|(\d+\.\d+ - \d+\.\d+))(, HHVM)?$/,
)
+/**
+ * Validates a short or full git commit hash (7 to 40 hexadecimal characters).
+ *
+ * @type {Joi.StringSchema}
+ */
+const isCommitHash = withRegex(/^[a-f0-9]{7,40}$/)
+
+/**
+ * Validates a 5-character star rating string composed of full, fractional,
+ * and empty star glyphs, e.g. `★★★¾☆`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isStarRating = withRegex(
- /^(?=.{5}$)(\u2605{0,5}[\u00BC\u00BD\u00BE]?\u2606{0,5})$/
+ /^(?=.{5}$)(\u2605{0,5}[\u00BC\u00BD\u00BE]?\u2606{0,5})$/,
)
-// Required to be > 0, because accepting zero masks many problems.
+/**
+ * Validates a metric-formatted positive number, e.g. `10`, `1k`, or `2.5M`.
+ * Required to be > 0, because accepting zero masks many problems.
+ *
+ * @type {Joi.StringSchema}
+ */
const isMetric = withRegex(/^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])$/)
-const isMetricOpenIssues = withRegex(
- /^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) open$/
+/**
+ * Same as {@link isMetric}, but also accepts zero and negative numbers,
+ * e.g. `0`, `-1k`, or `-2.5M`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricAllowNegative = withRegex(
+ /^(0|-?[1-9][0-9]*[kMGTPEZY]?|-?[0-9]\.[0-9][kMGTPEZY])$/,
)
-const isMetricOverMetric = withRegex(
- /^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])\/([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])$/
+/**
+ * Creates a validator that matches a metric (see {@link isMetric}) followed
+ * by another pattern, e.g. ` open` or `/year`.
+ *
+ * @param {RegExp} nestedRegexp Pattern that must appear after the metric
+ * @returns {Joi.StringSchema} Joi string schema for the combined pattern
+ */
+const isMetricWithPattern = nestedRegexp => {
+ const pattern = `^([1-9][0-9]*[kMGTPEZY]?|[1-9]\\.[1-9][kMGTPEZY])${nestedRegexp.source}$`
+ const regexp = new RegExp(pattern)
+ return withRegex(regexp)
+}
+
+/**
+ * Validates a metric followed by ` open`, e.g. `3 open` or `1.2k open`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricOpenIssues = isMetricWithPattern(/ open/)
+
+/**
+ * Validates a metric followed by ` closed`, e.g. `3 closed` or `1.2k closed`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricClosedIssues = isMetricWithPattern(/ closed/)
+
+/**
+ * Validates a metric followed by `/` and another metric, e.g. `3/10` or `1.2k/5k`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricOverMetric = isMetricWithPattern(
+ /\/([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])/,
)
-const isMetricOverTimePeriod = withRegex(
- /^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])\/(year|month|four weeks|week|day)$/
+/**
+ * Validates a metric followed by `/` and a time period, e.g. `3/day` or `1.2k/month`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricOverTimePeriod = isMetricWithPattern(
+ /\/(year|month|four weeks|quarter|week|day)/,
)
+/**
+ * Validates a literal zero followed by `/` and a time period, e.g. `0/day` or `0/year`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isZeroOverTimePeriod = withRegex(
+ /^0\/(year|month|four weeks|quarter|week|day)$/,
+)
+
+/**
+ * Validates a non-negative integer percentage, e.g. `0%`, `75%`, or `100%`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isIntegerPercentage = withRegex(/^[1-9][0-9]?%|^100%|^0%$/)
+/**
+ * Same as {@link isIntegerPercentage}, but also accepts negative values, e.g. `-25%`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isIntegerPercentageNegative = withRegex(/^-?[1-9][0-9]?%|^100%|^0%$/)
+/**
+ * Validates a non-negative decimal percentage, e.g. `0.5%` or `12.34%`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isDecimalPercentage = withRegex(/^[0-9]+\.[0-9]*%$/)
+/**
+ * Same as {@link isDecimalPercentage}, but also accepts negative values, e.g. `-0.5%`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isDecimalPercentageNegative = withRegex(/^-?[0-9]+\.[0-9]*%$/)
+/**
+ * Validates any percentage string by combining {@link isIntegerPercentage},
+ * {@link isDecimalPercentage}, {@link isIntegerPercentageNegative}, and
+ * {@link isDecimalPercentageNegative}.
+ *
+ * @type {Joi.AlternativesSchema}
+ */
const isPercentage = Joi.alternatives().try(
isIntegerPercentage,
- isDecimalPercentage
+ isDecimalPercentage,
+ isIntegerPercentageNegative,
+ isDecimalPercentageNegative,
)
-const isFileSize = withRegex(/^[0-9]*[.]?[0-9]+\s(B|kB|MB|GB|TB|PB|EB|ZB|YB)$/)
+/**
+ * Validates a metric (SI) file size, e.g. `1 B`, `12.5 kB`, or `2 MB`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isMetricFileSize = withRegex(
+ /^[0-9]*[.]?[0-9]+\s(B|kB|KB|MB|GB|TB|PB|EB|ZB|YB)$/,
+)
+/**
+ * Validates an IEC (binary) file size, e.g. `1 B`, `12.5 KiB`, or `2 MiB`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isIecFileSize = withRegex(
+ /^[0-9]*[.]?[0-9]+\s(B|KiB|MiB|GiB|TiB|PiB|EiB|ZiB|YiB)$/,
+)
+/**
+ * Validates a human-readable formatted date, such as `today`, `yesterday`,
+ * `last tuesday`, or `january 2021`.
+ *
+ * @type {Joi.AlternativesSchema}
+ */
const isFormattedDate = Joi.alternatives().try(
Joi.equal('today', 'yesterday'),
Joi.string().regex(/^last (sun|mon|tues|wednes|thurs|fri|satur)day$/),
Joi.string().regex(
- /^(january|february|march|april|may|june|july|august|september|october|november|december)( \d{4})?$/
- )
+ /^(january|february|march|april|may|june|july|august|september|october|november|december)( \d{4})?$/,
+ ),
)
+/**
+ * Validates a relative formatted date, e.g. `2 days ago`, `in 3 months`,
+ * or `a few seconds ago`.
+ *
+ * @type {Joi.AlternativesSchema}
+ */
const isRelativeFormattedDate = Joi.alternatives().try(
Joi.string().regex(
- /^(in |)([0-9]+|a few|a|an|)(| )(second|minute|hour|day|month|year)(s|)( ago|)$/
- )
+ /^(in |)([0-9]+|a few|a|an|)(| )(second|minute|hour|day|month|year)(s|)( ago|)$/,
+ ),
)
+/**
+ * Validates a dependency state string, e.g. `up to date`, `3 out of date`,
+ * or `2 deprecated`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isDependencyState = withRegex(
- /^(\d+ out of date|\d+ deprecated|up to date)$/
+ /^(\d+ out of date|\d+ deprecated|up to date)$/,
)
+/**
+ * Creates a validator for test totals in the format
+ * ` (, )?(, )?`, e.g. `5 passed, 1 failed`.
+ *
+ * @param {object} labels Label strings used for each outcome
+ * @param {string} labels.passed Label for passing tests
+ * @param {string} labels.failed Label for failing tests
+ * @param {string} labels.skipped Label for skipped tests
+ * @returns {Joi.StringSchema} Joi string schema for the test totals format
+ */
const makeTestTotalsValidator = ({ passed, failed, skipped }) =>
withRegex(
- new RegExp(`^[0-9]+ ${passed}(, [0-9]+ ${failed})?(, [0-9]+ ${skipped})?$`)
+ new RegExp(`^[0-9]+ ${passed}(, [0-9]+ ${failed})?(, [0-9]+ ${skipped})?$`),
)
+/**
+ * Creates a validator for test totals in the compact format
+ * ` ( | )?( | )?`, e.g. `✔ 5 | ✘ 1`.
+ *
+ * @param {object} labels Label strings used for each outcome
+ * @param {string} labels.passed Label for passing tests
+ * @param {string} labels.failed Label for failing tests
+ * @param {string} labels.skipped Label for skipped tests
+ * @returns {Joi.StringSchema} Joi string schema for the compact test totals format
+ */
const makeCompactTestTotalsValidator = ({ passed, failed, skipped }) =>
withRegex(
new RegExp(
- `^${passed} [0-9]+( \\| ${failed} [0-9]+)?( \\| ${skipped} [0-9]+)?$`
- )
+ `^${passed} [0-9]+( \\| ${failed} [0-9]+)?( \\| ${skipped} [0-9]+)?$`,
+ ),
)
+/**
+ * Validates a default test totals string with the labels `passed`, `failed`,
+ * and `skipped`, e.g. `5 passed, 1 failed, 2 skipped`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isDefaultTestTotals = makeTestTotalsValidator({
passed: 'passed',
failed: 'failed',
skipped: 'skipped',
})
+/**
+ * Validates a default compact test totals string with the glyph labels
+ * `✔`, `✘`, and `➟`, e.g. `✔ 5 | ✘ 1 | ➟ 2`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isDefaultCompactTestTotals = makeCompactTestTotalsValidator({
passed: '✔',
failed: '✘',
skipped: '➟',
})
+/**
+ * Validates a custom test totals string with the labels `good`, `bad`, and
+ * `n/a`, e.g. `5 good, 1 bad, 2 n/a`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isCustomTestTotals = makeTestTotalsValidator({
passed: 'good',
failed: 'bad',
skipped: 'n/a',
})
+/**
+ * Validates a custom compact test totals string with the emoji labels
+ * `💃`, `🤦♀️`, and `🤷`, e.g. `💃 5 | 🤦♀️ 1 | 🤷 2`.
+ *
+ * @type {Joi.StringSchema}
+ */
const isCustomCompactTestTotals = makeCompactTestTotalsValidator({
passed: '💃',
failed: '🤦♀️',
skipped: '🤷',
})
-module.exports = {
+/**
+ * Validates an ordinal number string with a superscript suffix, e.g. `1ˢᵗ`, `2ⁿᵈ`, or `11ᵗʰ`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/)
+/**
+ * Same as {@link isOrdinalNumber}, but followed by ` daily`, e.g. `1ˢᵗ daily`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isOrdinalNumberDaily = Joi.string().regex(
+ /^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/,
+)
+
+/**
+ * Validates a humanized duration string, e.g. `3 days`, `1 hour`, or `2 years`.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isHumanized = Joi.string().regex(
+ /[0-9a-z]+ (second|seconds|minute|minutes|hour|hours|day|days|month|months|year|years)/,
+)
+
+/**
+ * Validates a currency amount string with an optional `$` prefix, thousands
+ * separators, and up to two decimal places. For example, `$1,530,602.24` and
+ * `1,530,602.24` are valid, while `$1,666.24$`, `,1,666,88,`, `1.6.66,6`, and
+ * `.1555.` are not.
+ *
+ * @type {Joi.StringSchema}
+ */
+const isCurrency = withRegex(
+ /(?=.*\d)^\$?(([1-9]\d{0,2}(,\d{3})*)|0)?(\.\d{1,2})?$/,
+)
+
+export {
isSemver,
isVPlusTripleDottedVersion,
isVPlusDottedVersionAtLeastOne,
@@ -145,15 +390,21 @@ module.exports = {
isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch,
isComposerVersion,
isPhpVersionReduction,
+ isCommitHash,
isStarRating,
isMetric,
+ isMetricAllowNegative,
+ isMetricWithPattern,
isMetricOpenIssues,
+ isMetricClosedIssues,
isMetricOverMetric,
isMetricOverTimePeriod,
+ isZeroOverTimePeriod,
isPercentage,
isIntegerPercentage,
isDecimalPercentage,
- isFileSize,
+ isMetricFileSize,
+ isIecFileSize,
isFormattedDate,
isRelativeFormattedDate,
isDependencyState,
@@ -164,4 +415,8 @@ module.exports = {
isCustomCompactTestTotals,
makeTestTotalsValidator,
makeCompactTestTotalsValidator,
+ isOrdinalNumber,
+ isOrdinalNumberDaily,
+ isHumanized,
+ isCurrency,
}
diff --git a/services/tester.js b/services/tester.js
index c9d230b0f968c..1eead4670f29d 100644
--- a/services/tester.js
+++ b/services/tester.js
@@ -1,9 +1,4 @@
-'use strict'
+import createServiceTester from '../core/service-test-runner/create-service-tester.js'
+import ServiceTester from '../core/service-test-runner/service-tester.js'
-const createServiceTester = require('../core/service-test-runner/create-service-tester')
-const ServiceTester = require('../core/service-test-runner/service-tester')
-
-module.exports = {
- createServiceTester,
- ServiceTester,
-}
+export { createServiceTester, ServiceTester }
diff --git a/services/testspace/testspace-base.js b/services/testspace/testspace-base.js
new file mode 100644
index 0000000000000..e1fd3f300945f
--- /dev/null
+++ b/services/testspace/testspace-base.js
@@ -0,0 +1,62 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound } from '../index.js'
+
+// https://help.testspace.com/reference/web-api#list-results
+// case_counts|array|The contained cases [passed, failed, na, errored, untested]|counters of result
+// session_* fields are for manual runs
+// There are instances where the api returns a 200 status code with an empty array
+// notably in cases where a space id is used
+const schema = Joi.array()
+ .items(
+ Joi.object({
+ case_counts: Joi.array()
+ .items(nonNegativeInteger)
+ .min(5)
+ .max(5)
+ .required(),
+ }),
+ )
+ .required()
+
+// https://help.testspace.com/dashboard/overview#navigate
+// Org is owner/account
+// Project is generally a repository
+// Space is a container, often a branch
+export default class TestspaceBase extends BaseJsonService {
+ static category = 'test-results'
+ static defaultBadgeData = { label: 'tests' }
+
+ async fetch({ org, project, space }) {
+ // https://help.testspace.com/docs/reference/web-api#list-results
+ const url = `https://${org}.testspace.com/api/projects/${encodeURIComponent(
+ project,
+ )}/spaces/${space}/results`
+ return this._requestJson({
+ schema,
+ url,
+ httpErrors: {
+ 403: 'org not found or not authorized',
+ 404: 'org, project, or space not found',
+ },
+ options: {
+ dnsLookupIpVersion: 4,
+ },
+ })
+ }
+
+ transformCaseCounts(json) {
+ if (json.length === 0) {
+ throw new NotFound({ prettyMessage: 'space not found or results purged' })
+ }
+
+ const [
+ {
+ case_counts: [passed, failed, skipped, errored, untested],
+ },
+ ] = json
+ const total = passed + failed + skipped + errored + untested
+
+ return { passed, failed, skipped, errored, untested, total }
+ }
+}
diff --git a/services/testspace/testspace-base.spec.js b/services/testspace/testspace-base.spec.js
new file mode 100644
index 0000000000000..ff55a819653ce
--- /dev/null
+++ b/services/testspace/testspace-base.spec.js
@@ -0,0 +1,11 @@
+import { expect } from 'chai'
+import { NotFound } from '../index.js'
+import TestspaceBase from './testspace-base.js'
+
+describe('TestspaceBase', function () {
+ it('throws NotFound when response is missing space results', function () {
+ expect(() => TestspaceBase.prototype.transformCaseCounts([]))
+ .to.throw(NotFound)
+ .with.property('prettyMessage', 'space not found or results purged')
+ })
+})
diff --git a/services/testspace/testspace-test-count.service.js b/services/testspace/testspace-test-count.service.js
new file mode 100644
index 0000000000000..8594a88b97e86
--- /dev/null
+++ b/services/testspace/testspace-test-count.service.js
@@ -0,0 +1,76 @@
+import { pathParams } from '../index.js'
+import { metric as metricCount } from '../text-formatters.js'
+import TestspaceBase from './testspace-base.js'
+
+export default class TestspaceTestCount extends TestspaceBase {
+ static route = {
+ base: 'testspace',
+ pattern:
+ ':metric(total|passed|failed|skipped|errored|untested)/:org/:project/:space+',
+ }
+
+ static openApi = {
+ '/testspace/{metric}/{org}/{project}/{space}': {
+ get: {
+ summary: 'Testspace tests count',
+ parameters: pathParams(
+ {
+ name: 'metric',
+ example: 'passed',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ },
+ {
+ name: 'org',
+ example: 'swellaby',
+ },
+ {
+ name: 'project',
+ example: 'swellaby:testspace-sample',
+ },
+ {
+ name: 'space',
+ example: 'main',
+ },
+ ),
+ },
+ },
+ }
+
+ static render({ value, metric }) {
+ let color = 'informational'
+
+ if (metric === 'failed' || metric === 'errored') {
+ color = value === 0 ? 'success' : 'critical'
+ }
+
+ return {
+ label: `${metric} tests`,
+ message: metricCount(value),
+ color,
+ }
+ }
+
+ transform({ json, metric }) {
+ const { passed, failed, skipped, errored, untested, total } =
+ this.transformCaseCounts(json)
+ if (metric === 'total') {
+ return { value: total }
+ } else if (metric === 'passed') {
+ return { value: passed }
+ } else if (metric === 'failed') {
+ return { value: failed }
+ } else if (metric === 'skipped') {
+ return { value: skipped }
+ } else if (metric === 'untested') {
+ return { value: untested }
+ } else {
+ return { value: errored }
+ }
+ }
+
+ async handle({ metric, org, project, space }) {
+ const json = await this.fetch({ org, project, space })
+ const { value } = this.transform({ json, metric })
+ return this.constructor.render({ value, metric })
+ }
+}
diff --git a/services/testspace/testspace-test-count.spec.js b/services/testspace/testspace-test-count.spec.js
new file mode 100644
index 0000000000000..5876106e311d7
--- /dev/null
+++ b/services/testspace/testspace-test-count.spec.js
@@ -0,0 +1,47 @@
+import { test, given } from 'sazerac'
+import TestspaceTestCount from './testspace-test-count.service.js'
+
+describe('TestspaceTestCount', function () {
+ test(TestspaceTestCount.render, () => {
+ given({ metric: 'failed', value: 2 }).expect({
+ label: 'failed tests',
+ message: '2',
+ color: 'critical',
+ })
+ given({ metric: 'errored', value: 3 }).expect({
+ label: 'errored tests',
+ message: '3',
+ color: 'critical',
+ })
+ given({ metric: 'failed', value: 0 }).expect({
+ label: 'failed tests',
+ message: '0',
+ color: 'success',
+ })
+ given({ metric: 'errored', value: 0 }).expect({
+ label: 'errored tests',
+ message: '0',
+ color: 'success',
+ })
+ given({ metric: 'passed', value: 1 }).expect({
+ label: 'passed tests',
+ message: '1',
+ color: 'informational',
+ })
+ given({ metric: 'total', value: 8 }).expect({
+ label: 'total tests',
+ message: '8',
+ color: 'informational',
+ })
+ given({ metric: 'skipped', value: 0 }).expect({
+ label: 'skipped tests',
+ message: '0',
+ color: 'informational',
+ })
+ given({ metric: 'untested', value: 0 }).expect({
+ label: 'untested tests',
+ message: '0',
+ color: 'informational',
+ })
+ })
+})
diff --git a/services/testspace/testspace-test-count.tester.js b/services/testspace/testspace-test-count.tester.js
new file mode 100644
index 0000000000000..ce5c03d56ac84
--- /dev/null
+++ b/services/testspace/testspace-test-count.tester.js
@@ -0,0 +1,50 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+const isMetricAllowZero = Joi.alternatives(
+ isMetric,
+ Joi.number().valid(0).required(),
+)
+
+t.create('Total')
+ .get('/total/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'total tests',
+ message: isMetricAllowZero,
+ })
+
+t.create('Passed')
+ .get('/passed/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'passed tests',
+ message: isMetricAllowZero,
+ })
+
+t.create('Failed')
+ .get('/failed/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'failed tests',
+ message: isMetricAllowZero,
+ })
+
+t.create('Skipped')
+ .get('/skipped/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'skipped tests',
+ message: isMetricAllowZero,
+ })
+
+t.create('Errored')
+ .get('/errored/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'errored tests',
+ message: isMetricAllowZero,
+ })
+
+t.create('Untested')
+ .get('/untested/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'untested tests',
+ message: isMetricAllowZero,
+ })
diff --git a/services/testspace/testspace-test-pass-ratio.service.js b/services/testspace/testspace-test-pass-ratio.service.js
new file mode 100644
index 0000000000000..ade08aee21b1f
--- /dev/null
+++ b/services/testspace/testspace-test-pass-ratio.service.js
@@ -0,0 +1,51 @@
+import { pathParams } from '../index.js'
+import TestspaceBase from './testspace-base.js'
+
+export default class TestspacePassRatio extends TestspaceBase {
+ static route = {
+ base: 'testspace/pass-ratio',
+ pattern: ':org/:project/:space+',
+ }
+
+ static openApi = {
+ '/testspace/pass-ratio/{org}/{project}/{space}': {
+ get: {
+ summary: 'Testspace pass ratio',
+ parameters: pathParams(
+ {
+ name: 'org',
+ example: 'swellaby',
+ },
+ {
+ name: 'project',
+ example: 'swellaby:testspace-sample',
+ },
+ {
+ name: 'space',
+ example: 'main',
+ },
+ ),
+ },
+ },
+ }
+
+ static render({ passed, total }) {
+ const ratio = ((passed / total) * 100).toFixed(0)
+
+ return {
+ message: `${ratio}%`,
+ color: ratio === '100' ? 'success' : 'critical',
+ }
+ }
+
+ transform(json) {
+ const { passed, failed, errored } = this.transformCaseCounts(json)
+ return { passed, total: passed + failed + errored }
+ }
+
+ async handle({ org, project, space }) {
+ const json = await this.fetch({ org, project, space })
+ const { passed, total } = this.transform(json)
+ return this.constructor.render({ passed, total })
+ }
+}
diff --git a/services/testspace/testspace-test-pass-ratio.spec.js b/services/testspace/testspace-test-pass-ratio.spec.js
new file mode 100644
index 0000000000000..1aed0d418955e
--- /dev/null
+++ b/services/testspace/testspace-test-pass-ratio.spec.js
@@ -0,0 +1,16 @@
+import { test, given } from 'sazerac'
+import TestspacePassRatio from './testspace-test-pass-ratio.service.js'
+
+describe('TestspacePassRatio', function () {
+ test(TestspacePassRatio.render, () => {
+ given({ passed: 3, total: 5 }).expect({
+ message: '60%',
+ color: 'critical',
+ })
+
+ given({ passed: 4, total: 4 }).expect({
+ message: '100%',
+ color: 'success',
+ })
+ })
+})
diff --git a/services/testspace/testspace-test-pass-ratio.tester.js b/services/testspace/testspace-test-pass-ratio.tester.js
new file mode 100644
index 0000000000000..4d0de2d72abb0
--- /dev/null
+++ b/services/testspace/testspace-test-pass-ratio.tester.js
@@ -0,0 +1,24 @@
+import { createServiceTester } from '../tester.js'
+import { isIntegerPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Non-existent or unauthorized org')
+ .get('/swellaby1239017823123/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'tests',
+ message: 'org not found or not authorized',
+ })
+
+t.create('Non-existent project')
+ .get('/swellaby/swellaby:nope/main/baz.json')
+ .expectBadge({
+ label: 'tests',
+ message: 'org, project, or space not found',
+ })
+
+t.create('Valid project space')
+ .get('/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'tests',
+ message: isIntegerPercentage,
+ })
diff --git a/services/testspace/testspace-test-summary.service.js b/services/testspace/testspace-test-summary.service.js
new file mode 100644
index 0000000000000..ee19863702bf0
--- /dev/null
+++ b/services/testspace/testspace-test-summary.service.js
@@ -0,0 +1,56 @@
+import { pathParams } from '../index.js'
+import {
+ documentation as description,
+ testResultQueryParamSchema,
+ testResultOpenApiQueryParams,
+ renderTestResultBadge,
+} from '../test-results.js'
+import TestspaceBase from './testspace-base.js'
+
+export default class TestspaceTests extends TestspaceBase {
+ static route = {
+ base: 'testspace/tests',
+ pattern: ':org/:project/:space+',
+ queryParamSchema: testResultQueryParamSchema,
+ }
+
+ static openApi = {
+ '/testspace/tests/{org}/{project}/{space}': {
+ get: {
+ summary: 'Testspace tests',
+ description,
+ parameters: [
+ ...pathParams(
+ { name: 'org', example: 'swellaby' },
+ { name: 'project', example: 'swellaby:testspace-sample' },
+ { name: 'space', example: 'main' },
+ ),
+ ...testResultOpenApiQueryParams,
+ ],
+ },
+ },
+ }
+
+ async handle(
+ { org, project, space },
+ {
+ compact_message: compactMessage,
+ passed_label: passedLabel,
+ failed_label: failedLabel,
+ skipped_label: skippedLabel,
+ },
+ ) {
+ const json = await this.fetch({ org, project, space })
+ const { passed, failed, skipped, total } = this.transformCaseCounts(json)
+ return renderTestResultBadge({
+ passed,
+ failed,
+ skipped,
+ total,
+ isCompact: compactMessage !== undefined,
+ passedLabel,
+ failedLabel,
+ skippedLabel,
+ })
+ }
+}
diff --git a/services/testspace/testspace-test-summary.tester.js b/services/testspace/testspace-test-summary.tester.js
new file mode 100644
index 0000000000000..2a4bc4ccf02a5
--- /dev/null
+++ b/services/testspace/testspace-test-summary.tester.js
@@ -0,0 +1,50 @@
+import { createServiceTester } from '../tester.js'
+import {
+ isDefaultTestTotals,
+ isDefaultCompactTestTotals,
+ isCustomTestTotals,
+ isCustomCompactTestTotals,
+} from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Tests')
+ .get('/swellaby/swellaby:testspace-sample/main.json')
+ .expectBadge({
+ label: 'tests',
+ message: isDefaultTestTotals,
+ })
+
+t.create('Tests with compact message')
+ .get('/swellaby/swellaby:testspace-sample/main.json', {
+ qs: {
+ compact_message: null,
+ },
+ })
+ .expectBadge({
+ label: 'tests',
+ message: isDefaultCompactTestTotals,
+ })
+
+t.create('Tests with custom labels')
+ .get('/swellaby/swellaby:testspace-sample/main.json', {
+ qs: {
+ passed_label: 'good',
+ failed_label: 'bad',
+ skipped_label: 'n/a',
+ },
+ })
+ .expectBadge({ label: 'tests', message: isCustomTestTotals })
+
+t.create('Tests with compact message and custom labels')
+ .get('/swellaby/swellaby:testspace-sample/main.json', {
+ qs: {
+ compact_message: null,
+ passed_label: '💃',
+ failed_label: '🤦♀️',
+ skipped_label: '🤷',
+ },
+ })
+ .expectBadge({
+ label: 'tests',
+ message: isCustomCompactTestTotals,
+ })
diff --git a/services/text-formatters.js b/services/text-formatters.js
index 95cb600d4e3d0..95521a3b29dde 100644
--- a/services/text-formatters.js
+++ b/services/text-formatters.js
@@ -1,12 +1,20 @@
/**
* Commonly-used functions for formatting text in badge labels. Includes
* ordinal numbers, currency codes, star ratings, versions, etc.
+ *
+ * @module
*/
-'use strict'
-
-const moment = require('moment')
-moment().format()
+/**
+ * Creates a string of stars and empty stars based on the rating.
+ * The number of stars is determined by the integer part of the rating.
+ * An additional star or a three-quarter star or a half star or a quarter star is added based on the decimal part of the rating.
+ * The remaining stars are empty stars until the maximum number of stars is reached.
+ *
+ * @param {number} rating - Current rating
+ * @param {number} [max] - Maximum rating
+ * @returns {string} A string of stars and empty stars
+ */
function starRating(rating, max = 5) {
const flooredRating = Math.floor(rating)
let stars = ''
@@ -30,7 +38,13 @@ function starRating(rating, max = 5) {
return stars
}
-// Convert ISO 4217 code to unicode string.
+/**
+ * Converts the ISO 4217 code to the corresponding currency symbol.
+ * If the the symbol for the code is not found, then the code itself is returned.
+ *
+ * @param {string} code - ISO 4217 code
+ * @returns {string} Currency symbol for the code
+ */
function currencyFromCode(code) {
return (
{
@@ -42,40 +56,64 @@ function currencyFromCode(code) {
)
}
+/**
+ * Calculates the ordinal number of the given number.
+ * For example, if the input is 1, the output is “1ˢᵗ”.
+ *
+ * @param {number} n - Input number
+ * @returns {string} Ordinal number of the input number
+ */
function ordinalNumber(n) {
const s = ['ᵗʰ', 'ˢᵗ', 'ⁿᵈ', 'ʳᵈ']
const v = n % 100
return n + (s[(v - 20) % 10] || s[v] || s[0])
}
-// Given a number, string with appropriate unit in the metric system, SI.
-// Note: numbers beyond the peta- cannot be represented as integers in JS.
const metricPrefix = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
const metricPower = metricPrefix.map((a, i) => Math.pow(1000, i + 1))
+
+/**
+ * Given a number (positive or negative), returns a string with appropriate unit in the metric system, SI.
+ * Note: numbers beyond the peta- cannot be represented as integers in JS.
+ * For example, if you call metric(1000), it will return "1k", which means one kilo or one thousand.
+ *
+ * @param {number} n - Input number
+ * @returns {string} String with appropriate unit in the metric system, SI
+ */
function metric(n) {
for (let i = metricPrefix.length - 1; i >= 0; i--) {
const limit = metricPower[i]
- if (n >= limit) {
- const scaledN = n / limit
+ const absN = Math.abs(n)
+ if (absN >= limit) {
+ const scaledN = absN / limit
if (scaledN < 10) {
// For "small" numbers, display one decimal digit unless it is 0.
const oneDecimalN = scaledN.toFixed(1)
if (oneDecimalN.charAt(oneDecimalN.length - 1) !== '0') {
- return `${oneDecimalN}${metricPrefix[i]}`
+ const res = `${oneDecimalN}${metricPrefix[i]}`
+ return n > 0 ? res : `-${res}`
}
}
const roundedN = Math.round(scaledN)
if (roundedN < 1000) {
- return `${roundedN}${metricPrefix[i]}`
+ const res = `${roundedN}${metricPrefix[i]}`
+ return n > 0 ? res : `-${res}`
} else {
- return `1${metricPrefix[i + 1]}`
+ const res = `1${metricPrefix[i + 1]}`
+ return n > 0 ? res : `-${res}`
}
}
}
return `${n}`
}
-// Remove the starting v in a string.
+/**
+ * Remove the starting v in a string if it exists.
+ * For example, omitv("v1.2.3") returns "1.2.3", but omitv("hello") returns "hello".
+ *
+ * @param {string} version - Version string
+ * @returns {string} Version string without the starting v
+ */
function omitv(version) {
if (version.charCodeAt(0) === 118) {
return version.slice(1)
@@ -83,10 +121,16 @@ function omitv(version) {
return version
}
-// Add a starting v to the version unless:
-// - it does not start with a digit
-// - it is a date (yyyy-mm-dd)
-const ignoredVersionPatterns = /^[^0-9]|[0-9]{4}-[0-9]{2}-[0-9]{2}/
+const ignoredVersionPatterns =
+ /^[^0-9]|[0-9]{4}-[0-9]{2}-[0-9]{2}|^[a-f0-9]{7,40}$/
+
+/**
+ * Add a starting v to the version unless it doesn't starts with a digit, is a date (yyyy-mm-dd), or is a commit hash.
+ * For example, addv("1.2.3") returns "v1.2.3", but addv("hello"), addv("2021-10-31"), addv("abcdef1"), returns "hello", "2021-10-31", and "abcdef1" respectively.
+ *
+ * @param {string} version - Version string
+ * @returns {string} Version string with the starting v
+ */
function addv(version) {
version = `${version}`
if (version.startsWith('v') || ignoredVersionPatterns.test(version)) {
@@ -96,6 +140,15 @@ function addv(version) {
}
}
+/**
+ * Returns a string that is either the singular or the plural form of a word,
+ * depending on the length of the countable parameter.
+ *
+ * @param {string} singular - Singular form of the word
+ * @param {string[]} countable - Array of values you want to count
+ * @param {string} plural - Plural form of the word
+ * @returns {string} Singular or plural form of the word
+ */
function maybePluralize(singular, countable, plural) {
plural = plural || `${singular}s`
@@ -106,25 +159,7 @@ function maybePluralize(singular, countable, plural) {
}
}
-function formatDate(d) {
- const date = moment(d)
- const dateString = date.calendar(null, {
- lastDay: '[yesterday]',
- sameDay: '[today]',
- lastWeek: '[last] dddd',
- sameElse: 'MMMM YYYY',
- })
- // Trim current year from date string
- return dateString.replace(` ${moment().year()}`, '').toLowerCase()
-}
-
-function formatRelativeDate(timestamp) {
- return moment()
- .to(moment.unix(parseInt(timestamp, 10)))
- .toLowerCase()
-}
-
-module.exports = {
+export {
starRating,
currencyFromCode,
ordinalNumber,
@@ -132,6 +167,4 @@ module.exports = {
omitv,
addv,
maybePluralize,
- formatDate,
- formatRelativeDate,
}
diff --git a/services/text-formatters.spec.js b/services/text-formatters.spec.js
index 0326cfe87be01..9bdbd843ecbef 100644
--- a/services/text-formatters.spec.js
+++ b/services/text-formatters.spec.js
@@ -1,8 +1,5 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const sinon = require('sinon')
-const {
+import { test, given } from 'sazerac'
+import {
starRating,
currencyFromCode,
ordinalNumber,
@@ -10,11 +7,9 @@ const {
omitv,
addv,
maybePluralize,
- formatDate,
- formatRelativeDate,
-} = require('./text-formatters')
+} from './text-formatters.js'
-describe('Text formatters', function() {
+describe('Text formatters', function () {
test(starRating, () => {
given(4.9).expect('★★★★★')
given(3.7).expect('★★★¾☆')
@@ -40,6 +35,8 @@ describe('Text formatters', function() {
})
test(metric, () => {
+ /* eslint-disable no-loss-of-precision */
+ given(0).expect('0')
given(999).expect('999')
given(1000).expect('1k')
given(1100).expect('1.1k')
@@ -60,6 +57,28 @@ describe('Text formatters', function() {
given(1100000000000000000000).expect('1.1Z')
given(2222222222222222222222222).expect('2.2Y')
given(22222222222222222222222222).expect('22Y')
+ given(-999).expect('-999')
+ given(-999).expect('-999')
+ given(-1000).expect('-1k')
+ given(-1100).expect('-1.1k')
+ given(-10100).expect('-10k')
+ given(-999499).expect('-999k')
+ given(-999500).expect('-1M')
+ given(-1100000).expect('-1.1M')
+ given(-1578896212).expect('-1.6G')
+ given(-20000000000).expect('-20G')
+ given(-15788962120).expect('-16G')
+ given(-9949999999999).expect('-9.9T')
+ given(-9950000000001).expect('-10T')
+ given(-4000000000000001).expect('-4P')
+ given(-4200000000000001).expect('-4.2P')
+ given(-7100700010058000200).expect('-7.1E')
+ given(-71007000100580002000).expect('-71E')
+ given(-1000000000000000000000).expect('-1Z')
+ given(-1100000000000000000000).expect('-1.1Z')
+ given(-2222222222222222222222222).expect('-2.2Y')
+ given(-22222222222222222222222222).expect('-22Y')
+ /* eslint-enable */
})
test(omitv, () => {
@@ -74,6 +93,10 @@ describe('Text formatters', function() {
given('v0.6').expect('v0.6')
given('hello').expect('hello')
given('2017-05-05-Release-2.3.17').expect('2017-05-05-Release-2.3.17')
+ given('5aa272da7924fa76581fd5ea83b24cfbb3528b8a').expect(
+ '5aa272da7924fa76581fd5ea83b24cfbb3528b8a',
+ )
+ given('5aa272da79').expect('5aa272da79')
})
test(maybePluralize, () => {
@@ -87,48 +110,4 @@ describe('Text formatters', function() {
given('box', [123, 456], 'boxes').expect('boxes')
given('box', undefined, 'boxes').expect('boxes')
})
-
- test(formatDate, () => {
- given(1465513200000)
- .describe('when given a timestamp in june 2016')
- .expect('june 2016')
- })
-
- context('in october', function() {
- let clock
- beforeEach(function() {
- clock = sinon.useFakeTimers(new Date(2017, 9, 15).getTime())
- })
- afterEach(function() {
- clock.restore()
- })
-
- test(formatDate, () => {
- given(new Date(2017, 0, 1).getTime())
- .describe('when given the beginning of this year')
- .expect('january')
- })
- })
-
- context('in october', function() {
- let clock
- beforeEach(function() {
- clock = sinon.useFakeTimers(new Date(2018, 9, 29).getTime())
- })
- afterEach(function() {
- clock.restore()
- })
-
- test(formatRelativeDate, () => {
- given(new Date(2018, 9, 31).getTime() / 1000)
- .describe('when given the end of october')
- .expect('in 2 days')
- })
-
- test(formatRelativeDate, () => {
- given(new Date(2018, 9, 1).getTime() / 1000)
- .describe('when given the beginning of october')
- .expect('a month ago')
- })
- })
})
diff --git a/services/thunderstore/thunderstore-base.js b/services/thunderstore/thunderstore-base.js
new file mode 100644
index 0000000000000..a913935aec203
--- /dev/null
+++ b/services/thunderstore/thunderstore-base.js
@@ -0,0 +1,60 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+
+const packageMetricsSchema = Joi.object({
+ downloads: nonNegativeInteger,
+ rating_score: nonNegativeInteger,
+ latest_version: Joi.string().required(),
+})
+
+const description = `
+The Thunderstore badges require a package's namespace and name.
+
+Everything can be discerned from your package's URL. Thunderstore package URLs have a mostly consistent format:
+
+https://thunderstore.io/c/[community]/p/[namespace]/[packageName]
+
+For example: https://thunderstore.io/c/lethal-company/p/notnotnotswipez/MoreCompany/.
+
+ namespace = "notnotnotswipez"
+ packageName = "MoreCompany"
+
+
+:::info[Risk Of Rain 2]
+The 'default community', Risk of Rain 2, has an alternative URL:
+
+https://thunderstore.io/package/[namespace]/[packageName]
+:::
+
+:::info[Subdomain Communities]
+Some communities use a 'subdomain' alternative URL, for example, Valheim:
+
+https://valheim.thunderstore.io/package/[namespace]/[packageName]
+:::
+`
+
+/**
+ * Services which query Thunderstore endpoints should extend BaseThunderstoreService
+ *
+ * @abstract
+ */
+class BaseThunderstoreService extends BaseJsonService {
+ static thunderstoreGreen = '23FFB0'
+ /**
+ * Fetches package metrics from the Thunderstore API.
+ *
+ * @param {object} pkg - Package specifier
+ * @param {string} pkg.namespace - the package namespace
+ * @param {string} pkg.packageName - the package name
+ * @returns {Promise} - Promise containing validated package metrics
+ */
+ async fetchPackageMetrics({ namespace, packageName }) {
+ return this._requestJson({
+ schema: packageMetricsSchema,
+ url: `https://thunderstore.io/api/v1/package-metrics/${namespace}/${packageName}`,
+ })
+ }
+}
+
+export { BaseThunderstoreService, description }
diff --git a/services/thunderstore/thunderstore-downloads.service.js b/services/thunderstore/thunderstore-downloads.service.js
new file mode 100644
index 0000000000000..7dd4ae317ca6c
--- /dev/null
+++ b/services/thunderstore/thunderstore-downloads.service.js
@@ -0,0 +1,43 @@
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import { BaseThunderstoreService, description } from './thunderstore-base.js'
+
+export default class ThunderstoreDownloads extends BaseThunderstoreService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'thunderstore/dt',
+ pattern: ':namespace/:packageName',
+ }
+
+ static openApi = {
+ '/thunderstore/dt/{namespace}/{packageName}': {
+ get: {
+ summary: 'Thunderstore Downloads',
+ description,
+ parameters: pathParams(
+ { name: 'namespace', example: 'notnotnotswipez' },
+ { name: 'packageName', example: 'MoreCompany' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'downloads',
+ }
+
+ /**
+ * @param {object} pkg - Package specifier
+ * @param {string} pkg.namespace - the package namespace
+ * @param {string} pkg.packageName - the package name
+ * @returns {Promise} - Promise containing the rendered badge payload
+ */
+ async handle({ namespace, packageName }) {
+ const { downloads } = await this.fetchPackageMetrics({
+ namespace,
+ packageName,
+ })
+ return renderDownloadsBadge({ downloads })
+ }
+}
diff --git a/services/thunderstore/thunderstore-downloads.tester.js b/services/thunderstore/thunderstore-downloads.tester.js
new file mode 100644
index 0000000000000..7649798a4aef2
--- /dev/null
+++ b/services/thunderstore/thunderstore-downloads.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Downloads')
+ .get('/ebkr/r2modman.json')
+ .expectBadge({ label: 'downloads', message: isMetric })
+
+t.create('Downloads (not found)')
+ .get('/not-a-namespace/not-a-package-name.json')
+ .expectBadge({ label: 'downloads', message: 'not found', color: 'red' })
diff --git a/services/thunderstore/thunderstore-likes.service.js b/services/thunderstore/thunderstore-likes.service.js
new file mode 100644
index 0000000000000..f8970a2d82fa2
--- /dev/null
+++ b/services/thunderstore/thunderstore-likes.service.js
@@ -0,0 +1,52 @@
+import { metric } from '../text-formatters.js'
+import { pathParams } from '../index.js'
+import { BaseThunderstoreService, description } from './thunderstore-base.js'
+
+export default class ThunderstoreLikes extends BaseThunderstoreService {
+ static category = 'social'
+
+ static route = {
+ base: 'thunderstore/likes',
+ pattern: ':namespace/:packageName',
+ }
+
+ static openApi = {
+ '/thunderstore/likes/{namespace}/{packageName}': {
+ get: {
+ summary: 'Thunderstore Likes',
+ description,
+ parameters: pathParams(
+ { name: 'namespace', example: 'notnotnotswipez' },
+ { name: 'packageName', example: 'MoreCompany' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'likes',
+ namedLogo: 'thunderstore',
+ }
+
+ static render({ likes }) {
+ return {
+ message: metric(likes),
+ style: 'social',
+ color: `#${this.thunderstoreGreen}`,
+ }
+ }
+
+ /**
+ * @param {object} pkg - Package specifier
+ * @param {string} pkg.namespace - the package namespace
+ * @param {string} pkg.packageName - the package name
+ * @returns {Promise} - Promise containing the rendered badge payload
+ */
+ async handle({ namespace, packageName }) {
+ const { rating_score: likes } = await this.fetchPackageMetrics({
+ namespace,
+ packageName,
+ })
+ return this.constructor.render({ likes })
+ }
+}
diff --git a/services/thunderstore/thunderstore-likes.tester.js b/services/thunderstore/thunderstore-likes.tester.js
new file mode 100644
index 0000000000000..65af0a50144fb
--- /dev/null
+++ b/services/thunderstore/thunderstore-likes.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Likes')
+ .get('/ebkr/r2modman.json')
+ .expectBadge({ label: 'likes', message: isMetric })
+
+t.create('Likes (not found)')
+ .get('/not-a-namespace/not-a-package-name.json')
+ .expectBadge({ label: 'likes', message: 'not found', color: 'red' })
diff --git a/services/thunderstore/thunderstore-version.service.js b/services/thunderstore/thunderstore-version.service.js
new file mode 100644
index 0000000000000..25639928b85a8
--- /dev/null
+++ b/services/thunderstore/thunderstore-version.service.js
@@ -0,0 +1,43 @@
+import { renderVersionBadge } from '../version.js'
+import { pathParams } from '../index.js'
+import { BaseThunderstoreService, description } from './thunderstore-base.js'
+
+export default class ThunderstoreVersion extends BaseThunderstoreService {
+ static category = 'version'
+
+ static route = {
+ base: 'thunderstore/v',
+ pattern: ':namespace/:packageName',
+ }
+
+ static openApi = {
+ '/thunderstore/v/{namespace}/{packageName}': {
+ get: {
+ summary: 'Thunderstore Version',
+ description,
+ parameters: pathParams(
+ { name: 'namespace', example: 'notnotnotswipez' },
+ { name: 'packageName', example: 'MoreCompany' },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'thunderstore',
+ }
+
+ /**
+ * @param {object} pkg - Package specifier
+ * @param {string} pkg.namespace - the package namespace
+ * @param {string} pkg.packageName - the package name
+ * @returns {Promise} - Promise containing the rendered badge payload
+ */
+ async handle({ namespace, packageName }) {
+ const { latest_version: version } = await this.fetchPackageMetrics({
+ namespace,
+ packageName,
+ })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/thunderstore/thunderstore-version.tester.js b/services/thunderstore/thunderstore-version.tester.js
new file mode 100644
index 0000000000000..d3afc05fbc32f
--- /dev/null
+++ b/services/thunderstore/thunderstore-version.tester.js
@@ -0,0 +1,12 @@
+import { createServiceTester } from '../tester.js'
+import { isSemver } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('Version')
+ .get('/ebkr/r2modman.json')
+ .expectBadge({ label: 'thunderstore', message: isSemver })
+
+t.create('Version (not found)')
+ .get('/not-a-namespace/not-a-package-name.json')
+ .expectBadge({ label: 'thunderstore', message: 'not found', color: 'red' })
diff --git a/services/travis/travis-build.service.js b/services/travis/travis-build.service.js
index 324f4cf81314b..84ea31c15a60c 100644
--- a/services/travis/travis-build.service.js
+++ b/services/travis/travis-build.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseSvgScrapingService } = require('..')
+import Joi from 'joi'
+import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
+import { BaseSvgScrapingService, pathParams } from '../index.js'
const schema = Joi.object({
message: Joi.alternatives()
@@ -10,74 +8,64 @@ const schema = Joi.object({
.required(),
}).required()
-module.exports = class TravisBuild extends BaseSvgScrapingService {
- static get category() {
- return 'build'
- }
+export class TravisComBuild extends BaseSvgScrapingService {
+ static category = 'build'
- static get route() {
- return {
- base: 'travis',
- format: '(?:(com)/)?(?!php-v)([^/]+/[^/]+?)(?:/(.+?))?',
- capture: ['comDomain', 'userRepo', 'branch'],
- }
+ static route = {
+ base: 'travis/com',
+ pattern: ':user/:repo/:branch*',
}
- static get examples() {
- const { staticPreview } = this
- return [
- {
- title: 'Travis (.org)',
- pattern: ':user/:repo',
- namedParams: { user: 'rust-lang', repo: 'rust' },
- staticPreview,
- },
- {
- title: 'Travis (.org) branch',
- pattern: ':user/:repo/:branch',
- namedParams: { user: 'rust-lang', repo: 'rust', branch: 'master' },
- staticPreview,
+ static openApi = {
+ '/travis/com/{user}/{repo}': {
+ get: {
+ summary: 'Travis (.com)',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'ivandelabeldad',
+ },
+ {
+ name: 'repo',
+ example: 'rackian-gateway',
+ },
+ ),
},
- {
- title: 'Travis (.com)',
- pattern: 'com/:user/:repo',
- namedParams: { user: 'ivandelabeldad', repo: 'rackian-gateway' },
- staticPreview,
+ },
+ '/travis/com/{user}/{repo}/{branch}': {
+ get: {
+ summary: 'Travis (.com) branch',
+ parameters: pathParams(
+ {
+ name: 'user',
+ example: 'ivandelabeldad',
+ },
+ {
+ name: 'repo',
+ example: 'rackian-gateway',
+ },
+ {
+ name: 'branch',
+ example: 'master',
+ },
+ ),
},
- {
- title: 'Travis (.com) branch',
- pattern: 'com/:user/:repo/:branch',
- namedParams: {
- user: 'ivandelabeldad',
- repo: 'rackian-gateway',
- branch: 'master',
- },
- staticPreview,
- },
- ]
- }
-
- static get staticPreview() {
- return { message: 'passing', color: 'brightgreen' }
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
+ static defaultBadgeData = {
+ label: 'build',
}
static render({ status }) {
return renderBuildStatusBadge({ status })
}
- async handle({ comDomain, userRepo, branch }) {
- const domain = comDomain || 'org'
+ async handle({ user, repo, branch }) {
const { message: status } = await this._requestSvg({
schema,
- url: `https://api.travis-ci.${domain}/${userRepo}.svg`,
- options: { qs: { branch } },
- valueMatcher: />([^<>]+)<\/text><\/g>/,
+ url: `https://api.travis-ci.com/${user}/${repo}.svg`,
+ options: { searchParams: { branch } },
})
return this.constructor.render({ status })
diff --git a/services/travis/travis-build.tester.js b/services/travis/travis-build.tester.js
index 6bb0d988e9172..23fbc8e636b97 100644
--- a/services/travis/travis-build.tester.js
+++ b/services/travis/travis-build.tester.js
@@ -1,20 +1,17 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Travis (.org) CI
+import Joi from 'joi'
+import { isBuildStatus } from '../build-status.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('build status on default branch')
- .get('/rust-lang/rust.json')
+ .get('/ivandelabeldad/rackian-gateway.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('build status on named branch')
- .get('/rust-lang/rust/stable.json')
+ .get('/ivandelabeldad/rackian-gateway.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
@@ -27,37 +24,6 @@ t.create('unknown repo')
t.create('invalid svg response')
.get('/foo/bar.json')
.intercept(nock =>
- nock('https://api.travis-ci.org')
- .get('/foo/bar.svg')
- .reply(200)
- )
- .expectBadge({ label: 'build', message: 'unparseable svg response' })
-
-// Travis (.com) CI
-
-t.create('build status on default branch')
- .get('/com/ivandelabeldad/rackian-gateway.json')
- .expectBadge({
- label: 'build',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
-
-t.create('build status on named branch')
- .get('/com/ivandelabeldad/rackian-gateway.json')
- .expectBadge({
- label: 'build',
- message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
- })
-
-t.create('unknown repo')
- .get('/com/this-repo/does-not-exist.json')
- .expectBadge({ label: 'build', message: 'unknown' })
-
-t.create('invalid svg response')
- .get('/com/foo/bar.json')
- .intercept(nock =>
- nock('https://api.travis-ci.com')
- .get('/foo/bar.svg')
- .reply(200)
+ nock('https://api.travis-ci.com').get('/foo/bar.svg').reply(200),
)
.expectBadge({ label: 'build', message: 'unparseable svg response' })
diff --git a/services/travis/travis-php-version-redirect.service.js b/services/travis/travis-php-version-redirect.service.js
deleted file mode 100644
index ea0214fe4277b..0000000000000
--- a/services/travis/travis-php-version-redirect.service.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-module.exports = redirector({
- category: 'platform-support',
- route: {
- base: 'travis-ci/php-v',
- pattern: ':user/:repo/:branch*',
- },
- transformPath: ({ user, repo, branch }) =>
- branch
- ? `/travis/php-v/${user}/${repo}/${branch}`
- : `/travis/php-v/${user}/${repo}`,
- dateAdded: new Date('2019-04-22'),
-})
diff --git a/services/travis/travis-php-version-redirect.tester.js b/services/travis/travis-php-version-redirect.tester.js
deleted file mode 100644
index 7ef6dfb235882..0000000000000
--- a/services/travis/travis-php-version-redirect.tester.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'TravisPhpVersionRedirect',
- title: 'TravisPhpVersionRedirect',
- pathPrefix: '/travis-ci/php-v',
-}))
-
-t.create('travis-ci no branch')
- .get('/symfony/symfony.svg')
- .expectRedirect('/travis/php-v/symfony/symfony.svg')
-
-t.create('travis-ci branch')
- .get('/symfony/symfony/2.8.svg')
- .expectRedirect('/travis/php-v/symfony/symfony/2.8.svg')
diff --git a/services/travis/travis-php-version.service.js b/services/travis/travis-php-version.service.js
deleted file mode 100644
index 78a853707b2b0..0000000000000
--- a/services/travis/travis-php-version.service.js
+++ /dev/null
@@ -1,110 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- minorVersion,
- versionReduction,
- getPhpReleases,
-} = require('../php-version')
-const { BaseJsonService } = require('..')
-
-const optionalNumberOrString = Joi.alternatives(Joi.string(), Joi.number())
-const schema = Joi.object({
- branch: Joi.object({
- config: Joi.object({
- php: Joi.array().items(optionalNumberOrString),
- matrix: Joi.object({
- include: Joi.array().items(Joi.object({ php: optionalNumberOrString })),
- }),
- jobs: Joi.object({
- include: Joi.array().items(Joi.object({ php: optionalNumberOrString })),
- }),
- }).required(),
- }).required(),
-}).required()
-
-module.exports = class TravisPhpVersion extends BaseJsonService {
- static get category() {
- return 'platform-support'
- }
-
- static get route() {
- return {
- base: 'travis/php-v',
- pattern: ':user/:repo/:branch*',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'PHP from Travis config',
- namedParams: { user: 'symfony', repo: 'symfony' },
- staticPreview: this.render({ reduction: ['^7.1.3'] }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'php',
- }
- }
-
- static render({ reduction, hasHhvm }) {
- return {
- message: reduction.concat(hasHhvm ? ['HHVM'] : []).join(', '),
- color: 'blue',
- }
- }
-
- constructor(context, config) {
- super(context, config)
- this._githubApiProvider = context.githubApiProvider
- }
-
- async transform({ branch: { config } }) {
- let travisVersions = []
-
- // from php
- if (config.php) {
- travisVersions = travisVersions.concat(config.php.map(v => v.toString()))
- }
- // from matrix
- if (config.matrix && config.matrix.include) {
- travisVersions = travisVersions.concat(
- config.matrix.include.filter(v => 'php' in v).map(v => v.php.toString())
- )
- }
- // from jobs
- if (config.jobs && config.jobs.include) {
- travisVersions = travisVersions.concat(
- config.jobs.include.filter(v => 'php' in v).map(v => v.php.toString())
- )
- }
-
- const versions = travisVersions
- .map(v => minorVersion(v))
- .filter(v => v.includes('.'))
-
- return {
- reduction: versionReduction(
- versions,
- await getPhpReleases(this._githubApiProvider)
- ),
- hasHhvm: travisVersions.find(v => v.startsWith('hhvm')),
- }
- }
-
- async handle({ user, repo, branch = 'master' }) {
- const travisConfig = await this._requestJson({
- schema,
- url: `https://api.travis-ci.org/repos/${user}/${repo}/branches/${branch}`,
- errorMessages: {
- 404: 'repo not found',
- },
- })
- const { reduction, hasHhvm } = await this.transform(travisConfig)
- return this.constructor.render({ reduction, hasHhvm })
- }
-}
diff --git a/services/travis/travis-php-version.tester.js b/services/travis/travis-php-version.tester.js
deleted file mode 100644
index 7686406e011fb..0000000000000
--- a/services/travis/travis-php-version.tester.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const { isPhpVersionReduction } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('gets the package version of symfony')
- .get('/symfony/symfony.json')
- .expectBadge({ label: 'php', message: isPhpVersionReduction })
-
-t.create('gets the package version of symfony 2.8')
- .get('/symfony/symfony/2.8.json')
- .expectBadge({ label: 'php', message: isPhpVersionReduction })
-
-t.create('gets the package version of yii')
- .get('/yiisoft/yii.json')
- .expectBadge({ label: 'php', message: isPhpVersionReduction })
-
-t.create('gets the package version of pagination-bundle')
- .get('/gpslab/pagination-bundle.json')
- .expectBadge({ label: 'php', message: isPhpVersionReduction })
-
-t.create('invalid package name')
- .get('/frodo/is-not-a-package.json')
- .expectBadge({ label: 'php', message: 'repo not found' })
diff --git a/services/treeware/treeware-trees.service.js b/services/treeware/treeware-trees.service.js
index 7b4cefa542b59..e3a367cff44c9 100644
--- a/services/treeware/treeware-trees.service.js
+++ b/services/treeware/treeware-trees.service.js
@@ -1,39 +1,41 @@
-'use strict'
-
-const crypto = require('crypto')
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const { BaseJsonService } = require('..')
+import crypto from 'crypto'
+import Joi from 'joi'
+import { metric } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { BaseJsonService, pathParams } from '../index.js'
const apiSchema = Joi.object({
total: Joi.number().required(),
}).required()
-module.exports = class TreewareTrees extends BaseJsonService {
- static get category() {
- return 'other'
- }
+export default class TreewareTrees extends BaseJsonService {
+ static category = 'other'
- static get route() {
- return {
- base: 'treeware/trees',
- pattern: ':owner/:packageName',
- }
+ static route = {
+ base: 'treeware/trees',
+ pattern: ':owner/:packageName',
}
- static get examples() {
- return [
- {
- title: 'Treeware (Trees)',
- namedParams: { owner: 'stoplightio', packageName: 'spectral' },
- staticPreview: this.render({ count: 250 }),
+ static openApi = {
+ '/treeware/trees/{owner}/{packageName}': {
+ get: {
+ summary: 'Treeware (Trees)',
+ parameters: pathParams(
+ {
+ name: 'owner',
+ example: 'stoplightio',
+ },
+ {
+ name: 'packageName',
+ example: 'spectral',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'trees' }
+ static defaultBadgeData = {
+ label: 'trees',
}
static render({ count }) {
@@ -41,12 +43,12 @@ module.exports = class TreewareTrees extends BaseJsonService {
}
async fetch({ reference }) {
- const url = `https://public.offset.earth/users/treeware/trees`
+ const url = 'https://public.ecologi.com/users/treeware/trees'
return this._requestJson({
url,
schema: apiSchema,
options: {
- qs: { ref: reference },
+ searchParams: { ref: reference },
},
})
}
diff --git a/services/treeware/treeware-trees.tester.js b/services/treeware/treeware-trees.tester.js
index bc2d1a84f4736..df55ddeec41e5 100644
--- a/services/treeware/treeware-trees.tester.js
+++ b/services/treeware/treeware-trees.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
t.create('request for existing package')
.get('/stoplightio/spectral.json')
@@ -13,9 +12,9 @@ t.create('request for existing package')
t.create('request for existing package (mock)')
.get('/stoplightio/spectral.json')
.intercept(nock =>
- nock('https://public.offset.earth')
+ nock('https://public.ecologi.com')
.get('/users/treeware/trees?ref=65c6e3e942e7464b4591e0c8b70d11d5')
- .reply(200, { total: 50 })
+ .reply(200, { total: 50 }),
)
.expectBadge({
label: 'trees',
diff --git a/services/twitch/twitch-base.js b/services/twitch/twitch-base.js
index 1010f32f23872..2c34ef075957a 100644
--- a/services/twitch/twitch-base.js
+++ b/services/twitch/twitch-base.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
const tokenSchema = Joi.object({
access_token: Joi.string().required(),
@@ -16,14 +14,12 @@ function sleep(ms) {
}
// Abstract class for Twitch badges
-module.exports = class TwitchBase extends BaseJsonService {
- static get auth() {
- return {
- userKey: 'twitch_client_id',
- passKey: 'twitch_client_secret',
- authorizedOrigins: ['https://id.twitch.tv'],
- isRequired: true,
- }
+export default class TwitchBase extends BaseJsonService {
+ static auth = {
+ userKey: 'twitch_client_id',
+ passKey: 'twitch_client_secret',
+ authorizedOrigins: ['https://id.twitch.tv'],
+ isRequired: true,
}
constructor(...args) {
@@ -43,26 +39,26 @@ module.exports = class TwitchBase extends BaseJsonService {
{ userKey: 'client_id', passKey: 'client_secret' },
{
schema: tokenSchema,
- url: `https://id.twitch.tv/oauth2/token`,
+ url: 'https://id.twitch.tv/oauth2/token',
options: {
method: 'POST',
- qs: {
+ searchParams: {
grant_type: 'client_credentials',
},
},
- errorMessages: {
+ httpErrors: {
401: 'invalid token',
404: 'node not found',
},
- }
- )
+ },
+ ),
)
// replace the token when we are 80% near the expire time
// 2147483647 is the max 32-bit value that is accepted by setTimeout(), it's about 24.9 days
const replaceTokenMs = Math.min(
tokenRes.expires_in * 1000 * 0.8,
- 2147483647
+ 2147483647,
)
const timeout = setTimeout(() => {
TwitchBase.__twitchToken = this._getNewToken()
@@ -81,7 +77,7 @@ module.exports = class TwitchBase extends BaseJsonService {
options: {
...request.options,
headers: {
- 'Client-ID': this.authHelper.user,
+ 'Client-ID': this.authHelper._user,
Authorization: `Bearer ${await this._twitchToken()}`,
...(request.options && request.options.headers),
},
diff --git a/services/twitch/twitch-extension-version.service.js b/services/twitch/twitch-extension-version.service.js
new file mode 100644
index 0000000000000..9026e068b36c2
--- /dev/null
+++ b/services/twitch/twitch-extension-version.service.js
@@ -0,0 +1,54 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import TwitchBase from './twitch-base.js'
+
+const helixSchema = Joi.object({
+ data: Joi.array()
+ .items(Joi.object({ version: Joi.string().required() }).required())
+ .min(1)
+ .required(),
+})
+
+export default class TwitchExtensionVersion extends TwitchBase {
+ static category = 'version'
+
+ static route = {
+ base: 'twitch/extension/v',
+ pattern: ':extensionId',
+ }
+
+ static openApi = {
+ '/twitch/extension/v/{extensionId}': {
+ get: {
+ summary: 'Twitch Extension Version',
+ parameters: pathParams({
+ name: 'extensionId',
+ example: '2nq5cu1nc9f4p75b791w8d3yo9d195',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'twitch extension',
+ }
+
+ async fetch({ extensionId }) {
+ const data = this._requestJson({
+ schema: helixSchema,
+ url: 'https://api.twitch.tv/helix/extensions/released',
+ options: {
+ searchParams: { extension_id: extensionId },
+ },
+ })
+
+ return data
+ }
+
+ async handle({ extensionId }) {
+ const data = await this.fetch({ extensionId })
+
+ return renderVersionBadge({ version: data.data[0].version })
+ }
+}
diff --git a/services/twitch/twitch-extension-version.tester.js b/services/twitch/twitch-extension-version.tester.js
new file mode 100644
index 0000000000000..0983e33555e7b
--- /dev/null
+++ b/services/twitch/twitch-extension-version.tester.js
@@ -0,0 +1,16 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import _noTwitchToken from './twitch.service.js'
+const noTwitchToken = noToken(_noTwitchToken)
+export const t = await createServiceTester()
+
+t.create('gets the released version of Schedule with Google Calendar')
+ .skipWhen(noTwitchToken)
+ .get('/2nq5cu1nc9f4p75b791w8d3yo9d195.json')
+ .expectBadge({ label: 'twitch extension', message: isSemver })
+
+t.create('invalid extension id')
+ .skipWhen(noTwitchToken)
+ .get('/will-never-exist.json')
+ .expectBadge({ label: 'twitch extension', message: 'not found' })
diff --git a/services/twitch/twitch.service.js b/services/twitch/twitch.service.js
index 83ac000a5dc3d..ec76e76a0cf80 100644
--- a/services/twitch/twitch.service.js
+++ b/services/twitch/twitch.service.js
@@ -1,51 +1,42 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const TwitchBase = require('./twitch-base')
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import TwitchBase from './twitch-base.js'
const helixSchema = Joi.object({
data: Joi.array().required(),
})
-module.exports = class TwitchStatus extends TwitchBase {
- static get category() {
- return 'activity'
- }
+export default class TwitchStatus extends TwitchBase {
+ static category = 'social'
- static get route() {
- return {
- base: 'twitch/status',
- pattern: ':user',
- }
+ static route = {
+ base: 'twitch/status',
+ pattern: ':user',
}
- static get examples() {
- const preview = this.render({
- user: 'andyonthewings',
- isLive: true,
- })
- // link[] is not allowed in examples
- delete preview.link
- return [
- {
- title: 'Twitch Status',
- namedParams: {
- user: 'andyonthewings',
- },
- staticPreview: preview,
+ static openApi = {
+ '/twitch/status/{user}': {
+ get: {
+ summary: 'Twitch Status',
+ parameters: pathParams({
+ name: 'user',
+ example: 'andyonthewings',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'twitch',
- }
+ static _cacheLength = 30
+
+ static defaultBadgeData = {
+ label: 'twitch',
+ namedLogo: 'twitch',
}
static render({ user, isLive }) {
return {
message: isLive ? 'live' : 'offline',
+ style: 'social',
color: isLive ? 'red' : 'lightgrey',
link: `https://www.twitch.tv/${user}`,
}
@@ -59,9 +50,9 @@ module.exports = class TwitchStatus extends TwitchBase {
// which we consider not worth it.
const streams = this._requestJson({
schema: helixSchema,
- url: `https://api.twitch.tv/helix/streams`,
+ url: 'https://api.twitch.tv/helix/streams',
options: {
- qs: { user_login: user },
+ searchParams: { user_login: user },
},
})
diff --git a/services/twitch/twitch.spec.js b/services/twitch/twitch.spec.js
index 67567ddc218aa..ab418bcd48c5a 100644
--- a/services/twitch/twitch.spec.js
+++ b/services/twitch/twitch.spec.js
@@ -1,12 +1,10 @@
-'use strict'
+import { expect } from 'chai'
+import nock from 'nock'
+import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
+import TwitchStatus from './twitch.service.js'
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const TwitchStatus = require('./twitch.service')
-
-describe('TwitchStatus', function() {
- describe('auth', function() {
+describe('TwitchStatus', function () {
+ describe('auth', function () {
cleanUpNockAfterEach()
const user = 'admin'
@@ -19,7 +17,7 @@ describe('TwitchStatus', function() {
},
}
- it('sends the auth information as configured', async function() {
+ it('sends the auth information as configured', async function () {
const tokenNock = nock('https://id.twitch.tv')
.post('/oauth2/token')
// This ensures that the expected credentials are actually being sent with the HTTP request.
@@ -34,7 +32,9 @@ describe('TwitchStatus', function() {
expires_in: 2000000,
})
- const statusNock = nock('https://api.twitch.tv')
+ const statusNock = nock('https://api.twitch.tv', {
+ reqheaders: { Authorization: `Bearer ${token}`, 'Client-ID': user },
+ })
.get('/helix/streams')
.reply(200, {
data: [],
@@ -43,9 +43,10 @@ describe('TwitchStatus', function() {
expect(
await TwitchStatus.invoke(defaultContext, config, {
status: 'andyonthewings',
- })
+ }),
).to.deep.equal({
message: 'offline',
+ style: 'social',
link: 'https://www.twitch.tv/undefined',
color: 'lightgrey',
})
diff --git a/services/twitch/twitch.tester.js b/services/twitch/twitch.tester.js
index 54d8cadb451d1..9f68a1d82caae 100644
--- a/services/twitch/twitch.tester.js
+++ b/services/twitch/twitch.tester.js
@@ -1,14 +1,13 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import _noTwitchToken from './twitch.service.js'
+const noTwitchToken = noToken(_noTwitchToken)
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { noToken } = require('../test-helpers')
-const noTwitchToken = noToken(require('./twitch.service'))
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'twitch',
title: 'Twitch',
-}))
+})
// the first request would take longer since we need to wait for a token
t.create('Status of andyonthewings')
diff --git a/services/twitter/twitter-redirect.service.js b/services/twitter/twitter-redirect.service.js
index b7cacce78661d..ee802e17e0510 100644
--- a/services/twitter/twitter-redirect.service.js
+++ b/services/twitter/twitter-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = [
+export default [
redirector({
category: 'social',
name: 'TwitterUrlRedirect',
@@ -10,7 +8,7 @@ module.exports = [
base: 'twitter/url',
pattern: ':protocol(https|http)/:hostAndPath+',
},
- transformPath: () => `/twitter/url`,
+ transformPath: () => '/twitter/url',
transformQueryParams: ({ protocol, hostAndPath }) => ({
url: `${protocol}://${hostAndPath}`,
}),
diff --git a/services/twitter/twitter-redirect.tester.js b/services/twitter/twitter-redirect.tester.js
index 84d0f9a2895c4..4ecefd8e0fa91 100644
--- a/services/twitter/twitter-redirect.tester.js
+++ b/services/twitter/twitter-redirect.tester.js
@@ -1,15 +1,13 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'TwitterUrlRedirect',
title: 'TwitterUrlRedirect',
pathPrefix: '/twitter/url',
-}))
+})
t.create('twitter')
.get('/https/shields.io.svg')
.expectRedirect(
- `/twitter/url.svg?url=${encodeURIComponent('https://shields.io')}`
+ `/twitter/url.svg?url=${encodeURIComponent('https://shields.io')}`,
)
diff --git a/services/twitter/twitter.service.js b/services/twitter/twitter.service.js
index dd262729a39c2..dce025e06bd8a 100644
--- a/services/twitter/twitter.service.js
+++ b/services/twitter/twitter.service.js
@@ -1,50 +1,37 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { optionalUrl } = require('../validators')
-const { BaseService, BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseService, pathParams, queryParams } from '../index.js'
const queryParamSchema = Joi.object({
- url: optionalUrl.required(),
+ url,
}).required()
class TwitterUrl extends BaseService {
- static get category() {
- return 'social'
- }
+ static category = 'social'
- static get route() {
- return {
- base: 'twitter',
- pattern: 'url',
- queryParamSchema,
- }
+ static route = {
+ base: 'twitter',
+ pattern: 'url',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Twitter URL',
- namedParams: {},
- queryParams: {
- url: 'https://shields.io',
- },
- // hard code the static preview
- // because link[] is not allowed in examples
- staticPreview: {
- label: 'Tweet',
- message: '',
- style: 'social',
- },
+ static openApi = {
+ '/twitter/url': {
+ get: {
+ summary: 'X (formerly Twitter) URL',
+ parameters: queryParams({
+ name: 'url',
+ example: 'https://shields.io',
+ required: true,
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- namedLogo: 'twitter',
- }
+ static _cacheLength = 86400
+
+ static defaultBadgeData = {
+ namedLogo: 'x',
}
async handle(_routeParams, { url }) {
@@ -54,81 +41,63 @@ class TwitterUrl extends BaseService {
message: '',
style: 'social',
link: [
- `https://twitter.com/intent/tweet?text=Wow:&url=${page}`,
- `https://twitter.com/search?q=${page}`,
+ `https://x.com/intent/tweet?text=Wow:&url=${page}`,
+ `https://x.com/search?q=${page}`,
],
}
}
}
-const schema = Joi.any()
-
-class TwitterFollow extends BaseJsonService {
- static get category() {
- return 'social'
+/*
+This badge is unusual.
+
+We don't usually host badges that don't show any dynamic information.
+Also when an upstream API is removed, we usually deprecate/remove badges
+according to the process in
+https://github.com/badges/shields/blob/master/doc/retiring-badges.md
+
+In the case of twitter, we decided to provide a static fallback instead
+due to how widely used the badge was. See
+https://github.com/badges/shields/issues/8837
+for related discussion.
+*/
+class TwitterFollow extends BaseService {
+ static category = 'social'
+
+ static route = {
+ base: 'twitter/follow',
+ pattern: ':user',
}
- static get route() {
- return {
- base: 'twitter/follow',
- pattern: ':user',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Twitter Follow',
- namedParams: {
- user: 'espadrine',
- },
- queryParams: { label: 'Follow' },
- // hard code the static preview
- // because link[] is not allowed in examples
- staticPreview: {
- label: 'Follow',
- message: '393',
- style: 'social',
- },
+ static openApi = {
+ '/twitter/follow/{user}': {
+ get: {
+ summary: 'X (formerly Twitter) Follow',
+ parameters: pathParams({ name: 'user', example: 'shields_io' }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- namedLogo: 'twitter',
- }
+ static _cacheLength = 86400
+
+ static defaultBadgeData = {
+ namedLogo: 'x',
}
- static render({ user, followers }) {
+ static render({ user }) {
return {
label: `follow @${user}`,
- message: metric(followers),
+ message: '',
style: 'social',
link: [
- `https://twitter.com/intent/follow?screen_name=${encodeURIComponent(
- user
- )}`,
- `https://twitter.com/${encodeURIComponent(user)}/followers`,
+ `https://x.com/intent/follow?screen_name=${encodeURIComponent(user)}`,
],
}
}
- async fetch({ user }) {
- return this._requestJson({
- schema,
- url: `http://cdn.syndication.twimg.com/widgets/followbutton/info.json`,
- options: { qs: { screen_names: user } },
- })
- }
-
async handle({ user }) {
- const data = await this.fetch({ user })
- if (!Array.isArray(data) || data.length === 0) {
- throw new NotFound({ prettyMessage: 'invalid user' })
- }
- return this.constructor.render({ user, followers: data[0].followers_count })
+ return this.constructor.render({ user })
}
}
-module.exports = [TwitterUrl, TwitterFollow]
+export default [TwitterUrl, TwitterFollow]
diff --git a/services/twitter/twitter.tester.js b/services/twitter/twitter.tester.js
index 50941c9838176..4449f735d9dca 100644
--- a/services/twitter/twitter.tester.js
+++ b/services/twitter/twitter.tester.js
@@ -1,36 +1,16 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { isMetric } = require('../test-validators')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'twitter',
title: 'Twitter',
-}))
+})
t.create('Followers')
.get('/follow/shields_io.json')
.expectBadge({
label: 'follow @shields_io',
- message: isMetric,
- link: [
- 'https://twitter.com/intent/follow?screen_name=shields_io',
- 'https://twitter.com/shields_io/followers',
- ],
- })
-
-t.create('Invalid Username Specified (non-existent user)')
- .get('/follow/invalidusernamethatshouldnotexist.json?label=Follow')
- .expectBadge({
- label: 'Follow',
- message: 'invalid user',
- })
-
-t.create('Invalid Username Specified (only spaces)')
- .get('/follow/%20%20.json?label=Follow')
- .expectBadge({
- label: 'Follow',
- message: 'invalid user',
+ message: '',
+ link: ['https://x.com/intent/follow?screen_name=shields_io'],
})
t.create('URL')
@@ -39,7 +19,7 @@ t.create('URL')
label: 'tweet',
message: '',
link: [
- 'https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fshields.io',
- 'https://twitter.com/search?q=https%3A%2F%2Fshields.io',
+ 'https://x.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fshields.io',
+ 'https://x.com/search?q=https%3A%2F%2Fshields.io',
],
})
diff --git a/services/ubuntu/ubuntu.service.js b/services/ubuntu/ubuntu.service.js
index 746b703558e07..0ae4d3c4dbd60 100644
--- a/services/ubuntu/ubuntu.service.js
+++ b/services/ubuntu/ubuntu.service.js
@@ -1,50 +1,61 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const { BaseJsonService, NotFound } = require('..')
+import Joi from 'joi'
+import { renderVersionBadge } from '../version.js'
+import { BaseJsonService, NotFound, pathParams } from '../index.js'
const schema = Joi.object({
entries: Joi.array()
.items(
Joi.object({
source_package_version: Joi.string().required(),
- })
+ }),
)
.required(),
}).required()
-module.exports = class Ubuntu extends BaseJsonService {
- static get category() {
- return 'version'
- }
+export default class Ubuntu extends BaseJsonService {
+ static category = 'version'
- static get route() {
- return {
- base: 'ubuntu/v',
- pattern: ':packageName/:series?',
- }
+ static route = {
+ base: 'ubuntu/v',
+ pattern: ':packageName/:series?',
}
- static get examples() {
- return [
- {
- title: 'Ubuntu package',
- namedParams: { series: 'bionic', packageName: 'ubuntu-wallpapers' },
- staticPreview: renderVersionBadge({ version: '18.04.1-0ubuntu1' }),
+ static openApi = {
+ '/ubuntu/v/{packageName}/{series}': {
+ get: {
+ summary: 'Ubuntu Package Version (for series)',
+ parameters: pathParams(
+ {
+ name: 'packageName',
+ example: 'ubuntu-wallpapers',
+ },
+ {
+ name: 'series',
+ example: 'bionic',
+ },
+ ),
+ },
+ },
+ '/ubuntu/v/{packageName}': {
+ get: {
+ summary: 'Ubuntu Package Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'ubuntu-wallpapers',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'ubuntu' }
+ static defaultBadgeData = {
+ label: 'ubuntu',
}
async fetch({ packageName, series }) {
const seriesParam = series
? {
distro_series: `https://api.launchpad.net/1.0/ubuntu/${encodeURIComponent(
- series
+ series,
)}`,
}
: {}
@@ -52,7 +63,7 @@ module.exports = class Ubuntu extends BaseJsonService {
schema,
url: 'https://api.launchpad.net/1.0/ubuntu/+archive/primary',
options: {
- qs: {
+ searchParams: {
'ws.op': 'getPublishedSources',
exact_match: 'true',
order_by_date: 'true',
@@ -61,7 +72,7 @@ module.exports = class Ubuntu extends BaseJsonService {
...seriesParam,
},
},
- errorMessages: {
+ httpErrors: {
400: 'series not found',
},
})
diff --git a/services/ubuntu/ubuntu.tester.js b/services/ubuntu/ubuntu.tester.js
index 8b9371ec4c374..16f5228f5f67f 100644
--- a/services/ubuntu/ubuntu.tester.js
+++ b/services/ubuntu/ubuntu.tester.js
@@ -1,10 +1,9 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isUbuntuVersion = Joi.string().regex(
- /^v(\d+:)?\d+(\.\d+)*([\w\\.]*)?([-+~].*)?$/
+ /^v(\d+:)?\d+(\.\d+)*([\w\\.]*)?([-+~].*)?$/,
)
t.create('Ubuntu package (default distribution, valid)')
@@ -19,7 +18,7 @@ t.create('Ubuntu package (valid)')
.intercept(nock =>
nock('https://api.launchpad.net')
.get(
- '/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&exact_match=true&order_by_date=true&status=Published&source_name=ubuntu-wallpapers&distro_series=https%3A%2F%2Fapi.launchpad.net%2F1.0%2Fubuntu%2Fbionic'
+ '/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&exact_match=true&order_by_date=true&status=Published&source_name=ubuntu-wallpapers&distro_series=https%3A%2F%2Fapi.launchpad.net%2F1.0%2Fubuntu%2Fbionic',
)
.reply(200, {
entries: [
@@ -28,7 +27,7 @@ t.create('Ubuntu package (valid)')
source_package_version: '18.04.1-0ubuntu1',
},
],
- })
+ }),
)
.expectBadge({ label: 'ubuntu', message: 'v18.04.1-0ubuntu1' })
diff --git a/services/uptimeobserver/uptimeobserver-base.js b/services/uptimeobserver/uptimeobserver-base.js
new file mode 100644
index 0000000000000..fa1676661073e
--- /dev/null
+++ b/services/uptimeobserver/uptimeobserver-base.js
@@ -0,0 +1,56 @@
+import Joi from 'joi'
+import { BaseJsonService, InvalidParameter, InvalidResponse } from '../index.js'
+
+const errorResponse = Joi.object({
+ error: Joi.string().required(),
+}).required()
+
+const monitorResponse = Joi.object({
+ status: Joi.string().required(),
+ uptime24h: Joi.number().min(0).max(100).required(),
+ uptime7d: Joi.number().min(0).max(100).required(),
+ uptime30d: Joi.number().min(0).max(100).required(),
+}).required()
+
+const singleMonitorResponse = Joi.alternatives(monitorResponse, errorResponse)
+
+export default class UptimeObserverBase extends BaseJsonService {
+ static category = 'monitoring'
+
+ static ensureIsMonitorApiKey(value) {
+ if (!value) {
+ throw new InvalidParameter({
+ prettyMessage: 'monitor API key is required',
+ })
+ } else if (value.length <= 32) {
+ throw new InvalidParameter({
+ prettyMessage: 'monitor API key is unvalid',
+ })
+ }
+ }
+
+ async fetch({ monitorKey }) {
+ this.constructor.ensureIsMonitorApiKey(monitorKey)
+
+ // Docs for API: https://support.uptimeobserver.com/shields-api.yaml
+ const url = `https://app.uptimeobserver.com/api/monitor/status/${monitorKey}`
+
+ const response = await this._requestJson({
+ schema: singleMonitorResponse,
+ url,
+ options: {
+ method: 'GET',
+ },
+ logErrors: [],
+ })
+
+ // Handle error responses
+ if (response.error) {
+ throw new InvalidResponse({
+ prettyMessage: response.error || 'service error',
+ })
+ }
+
+ return response
+ }
+}
diff --git a/services/uptimeobserver/uptimeobserver-ratio.service.js b/services/uptimeobserver/uptimeobserver-ratio.service.js
new file mode 100644
index 0000000000000..edc16a165bbaf
--- /dev/null
+++ b/services/uptimeobserver/uptimeobserver-ratio.service.js
@@ -0,0 +1,80 @@
+import { pathParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import UptimeObserverBase from './uptimeobserver-base.js'
+
+const ratioColor = colorScale([10, 30, 50, 70])
+
+export default class UptimeObserverRatio extends UptimeObserverBase {
+ static route = {
+ base: 'uptimeobserver/ratio',
+ pattern: ':period(\\d+)?/:monitorKey',
+ }
+
+ static openApi = {
+ '/uptimeobserver/ratio/1/{monitorKey}': {
+ get: {
+ summary: 'UptimeObserver uptime ratio (1 day)',
+ parameters: pathParams({
+ name: 'monitorKey',
+ example: '33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41',
+ }),
+ },
+ },
+ '/uptimeobserver/ratio/7/{monitorKey}': {
+ get: {
+ summary: 'UptimeObserver uptime ratio (7 days)',
+ parameters: pathParams({
+ name: 'monitorKey',
+ example: '33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41',
+ }),
+ },
+ },
+ '/uptimeobserver/ratio/30/{monitorKey}': {
+ get: {
+ summary: 'UptimeObserver uptime ratio (30 days)',
+ parameters: pathParams({
+ name: 'monitorKey',
+ example: '33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'uptime',
+ }
+
+ static render({ ratio }) {
+ // Remove trailing zeros, show up to 3 decimal places
+ const formatted = Number(ratio).toLocaleString(undefined, {
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 3,
+ })
+ return {
+ message: `${formatted}%`,
+ color: ratioColor(ratio),
+ }
+ }
+
+ async handle({ period = '30', monitorKey }) {
+ const response = await this.fetch({ monitorKey })
+
+ let uptimeField
+ switch (period) {
+ case '1':
+ uptimeField = 'uptime24h'
+ break
+ case '7':
+ uptimeField = 'uptime7d'
+ break
+ case '30':
+ default:
+ uptimeField = 'uptime30d'
+ break
+ }
+
+ const ratio = response[uptimeField]
+
+ return this.constructor.render({ ratio })
+ }
+}
diff --git a/services/uptimeobserver/uptimeobserver-ratio.tester.js b/services/uptimeobserver/uptimeobserver-ratio.tester.js
new file mode 100644
index 0000000000000..e3dfc8f13b3fa
--- /dev/null
+++ b/services/uptimeobserver/uptimeobserver-ratio.tester.js
@@ -0,0 +1,113 @@
+import { createServiceTester } from '../tester.js'
+import { isPercentage } from '../test-validators.js'
+import { invalidJSON } from '../response-fixtures.js'
+
+export const t = await createServiceTester()
+
+t.create('UptimeObserver uptime ratio 30 days (mock)')
+ .get('/30/94e1d60553b1425ab2a276128f3bca7466.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/94e1d60553b1425ab2a276128f3bca7466')
+ .reply(200, {
+ status: 'UP',
+ friendlyName: 'Test Monitor',
+ lastExecution: '2025-06-04T18:17:06.171106Z',
+ uptime24h: 99.5,
+ uptime7d: 98.2,
+ uptime30d: 97.8,
+ }),
+ )
+ .expectBadge({
+ label: 'uptime',
+ message: '97.8%',
+ color: 'brightgreen',
+ })
+
+t.create('UptimeObserver uptime ratio 30 days')
+ .get('/30/33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41.json')
+ .expectBadge({
+ label: 'uptime',
+ message: isPercentage,
+ })
+
+t.create('UptimeObserver uptime ratio 7 days (mock)')
+ .get('/7/94e1d60553b1425ab2a276128f3bca7466.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/94e1d60553b1425ab2a276128f3bca7466')
+ .reply(200, {
+ status: 'UP',
+ friendlyName: 'Test Monitor',
+ lastExecution: '2025-06-04T18:17:06.171106Z',
+ uptime24h: 99.5,
+ uptime7d: 98.2,
+ uptime30d: 97.8,
+ }),
+ )
+ .expectBadge({
+ label: 'uptime',
+ message: '98.2%',
+ color: 'brightgreen',
+ })
+
+t.create('UptimeObserver uptime ratio 7 days')
+ .get('/7/33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41.json')
+ .expectBadge({
+ label: 'uptime',
+ message: isPercentage,
+ })
+
+t.create('UptimeObserver uptime ratio 24h (mock)')
+ .get('/1/94e1d60553b1425ab2a276128f3bca7466.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/94e1d60553b1425ab2a276128f3bca7466')
+ .reply(200, {
+ status: 'UP',
+ friendlyName: 'Test Monitor',
+ lastExecution: '2025-06-04T18:17:06.171106Z',
+ uptime24h: 99.5,
+ uptime7d: 98.2,
+ uptime30d: 97.8,
+ }),
+ )
+ .expectBadge({
+ label: 'uptime',
+ message: '99.5%',
+ color: 'brightgreen',
+ })
+
+t.create('UptimeObserver uptime ratio 24h')
+ .get('/1/33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41.json')
+ .expectBadge({
+ label: 'uptime',
+ message: isPercentage,
+ })
+
+t.create('UptimeObserver: Percentage (service unavailable)')
+ .get('/1/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741')
+ .reply(503, '{"error": "oh noes!!"}'),
+ )
+ .expectBadge({ label: 'uptime', message: 'inaccessible' })
+
+t.create('UptimeObserver: Percentage (unexpected response, valid json)')
+ .get('/1/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741')
+ .reply(200, '[]'),
+ )
+ .expectBadge({ label: 'uptime', message: 'invalid response data' })
+
+t.create('UptimeObserver: Percentage (unexpected response, valid json)')
+ .get('/1/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/22Zw1rnH6veb4OLcskqvj6g9Lj4tnyx741')
+ .reply(invalidJSON),
+ )
+ .expectBadge({ label: 'uptime', message: 'unparseable json response' })
diff --git a/services/uptimeobserver/uptimeobserver-status.service.js b/services/uptimeobserver/uptimeobserver-status.service.js
new file mode 100644
index 0000000000000..1ffba7bf7d461
--- /dev/null
+++ b/services/uptimeobserver/uptimeobserver-status.service.js
@@ -0,0 +1,71 @@
+import { pathParam } from '../index.js'
+import { queryParamSchema, queryParams } from '../website-status.js'
+import UptimeObserverBase from './uptimeobserver-base.js'
+
+export default class UptimeObserverStatus extends UptimeObserverBase {
+ static route = {
+ base: 'uptimeobserver/status',
+ pattern: ':monitorKey',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/uptimeobserver/status/{monitorKey}': {
+ get: {
+ summary: 'UptimeObserver status',
+ parameters: [
+ pathParam({
+ name: 'monitorKey',
+ example: '33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41',
+ }),
+ ...queryParams,
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'status',
+ }
+
+ static render({
+ status,
+ upMessage = 'up',
+ downMessage = 'down',
+ upColor = 'brightgreen',
+ downColor = 'red',
+ }) {
+ // Based on the API response, status values are strings like "UP", "DOWN", etc.
+ switch (status.toUpperCase()) {
+ case 'UP':
+ return { message: upMessage, color: upColor }
+ case 'DOWN':
+ return { message: downMessage, color: downColor }
+ case 'PAUSED':
+ return { message: 'paused', color: 'lightgrey' }
+ default:
+ return { message: status.toLowerCase(), color: 'lightgrey' }
+ }
+ }
+
+ async handle(
+ { monitorKey },
+ {
+ up_message: upMessage,
+ down_message: downMessage,
+ up_color: upColor,
+ down_color: downColor,
+ },
+ ) {
+ const response = await this.fetch({ monitorKey })
+ const { status } = response
+
+ return this.constructor.render({
+ status,
+ upMessage,
+ downMessage,
+ upColor,
+ downColor,
+ })
+ }
+}
diff --git a/services/uptimeobserver/uptimeobserver-status.tester.js b/services/uptimeobserver/uptimeobserver-status.tester.js
new file mode 100644
index 0000000000000..d3c9b43cee9ff
--- /dev/null
+++ b/services/uptimeobserver/uptimeobserver-status.tester.js
@@ -0,0 +1,74 @@
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+const isUptimeStatus = Joi.string().valid('up', 'down', 'paused')
+
+t.create('UptimeObserver status (mock)')
+ .get('/94e1d60553b1425ab2a276128f3bca7466.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/94e1d60553b1425ab2a276128f3bca7466')
+ .reply(200, {
+ status: 'UP',
+ friendlyName: 'Test Monitor',
+ lastExecution: '2025-06-04T18:17:06.171106Z',
+ uptime24h: 99.5,
+ uptime7d: 98.2,
+ uptime30d: 97.8,
+ }),
+ )
+ .expectBadge({
+ label: 'status',
+ message: 'up',
+ color: 'brightgreen',
+ })
+
+t.create('UptimeObserver uptime status (known monitor)')
+ .get('/33Zw1rnH6veb4OLcskqvj6g9Lj4tnyxZ41.json')
+ .expectBadge({
+ label: 'status',
+ message: isUptimeStatus,
+ })
+
+t.create('UptimeObserver uptime status (paused monitor)')
+ .get('/d48f3768a01a4f36972efebb096cb6c01933.json')
+ .expectBadge({
+ label: 'status',
+ message: isUptimeStatus,
+ })
+
+t.create('UptimeObserver uptime status (not found)')
+ .get('/aae1d60553b1425ab2a276128f3bca7466.json')
+ .expectBadge({
+ label: 'status',
+ message: 'not found',
+ })
+
+t.create('UptimeObserver uptime status (unvalid key)')
+ .get('/aae1d60553b1425ab2a.json')
+ .expectBadge({
+ label: 'status',
+ message: 'monitor API key is unvalid',
+ })
+
+t.create('UptimeObserver status DOWN (mock)')
+ .get('/94e1d60553b1425ab2a276128f3bca7466.json')
+ .intercept(nock =>
+ nock('https://app.uptimeobserver.com')
+ .get('/api/monitor/status/94e1d60553b1425ab2a276128f3bca7466')
+ .reply(200, {
+ status: 'DOWN',
+ friendlyName: 'Test Monitor Down',
+ lastExecution: '2025-06-04T18:17:06.171106Z',
+ uptime24h: 85.0,
+ uptime7d: 88.2,
+ uptime30d: 92.1,
+ }),
+ )
+ .expectBadge({
+ label: 'status',
+ message: 'down',
+ color: 'red',
+ })
diff --git a/services/uptimerobot/uptimerobot-base.js b/services/uptimerobot/uptimerobot-base.js
index 641237ac587bf..0d5b1506986b0 100644
--- a/services/uptimerobot/uptimerobot-base.js
+++ b/services/uptimerobot/uptimerobot-base.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService, InvalidParameter, InvalidResponse } = require('..')
+import Joi from 'joi'
+import { BaseJsonService, InvalidParameter, InvalidResponse } from '../index.js'
// https://uptimerobot.com/api
// POST getMonitors
@@ -26,28 +24,20 @@ const singleMonitorResponse = Joi.alternatives(
errorResponse,
Joi.object({
stat: Joi.equal('ok').required(),
- monitors: Joi.array()
- .length(1)
- .items(monitor)
- .required(),
- }).required()
+ monitors: Joi.array().length(1).items(monitor).required(),
+ }).required(),
)
const singleMonitorResponseWithUptime = Joi.alternatives(
errorResponse,
Joi.object({
stat: Joi.equal('ok').required(),
- monitors: Joi.array()
- .length(1)
- .items(monitorWithUptime)
- .required(),
- }).required()
+ monitors: Joi.array().length(1).items(monitorWithUptime).required(),
+ }).required(),
)
-module.exports = class UptimeRobotBase extends BaseJsonService {
- static get category() {
- return 'monitoring'
- }
+export default class UptimeRobotBase extends BaseJsonService {
+ static category = 'monitoring'
static ensureIsMonitorApiKey(value) {
// A monitor API key must start with "m".
@@ -84,6 +74,7 @@ module.exports = class UptimeRobotBase extends BaseJsonService {
...opts,
},
},
+ logErrors: [],
})
if (stat === 'fail') {
diff --git a/services/uptimerobot/uptimerobot-ratio.service.js b/services/uptimerobot/uptimerobot-ratio.service.js
index 377a00343a3e9..5b1d36eaf8ed8 100644
--- a/services/uptimerobot/uptimerobot-ratio.service.js
+++ b/services/uptimerobot/uptimerobot-ratio.service.js
@@ -1,43 +1,38 @@
-'use strict'
-
-const { colorScale } = require('../color-formatters')
-const UptimeRobotBase = require('./uptimerobot-base')
+import { pathParams } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import UptimeRobotBase from './uptimerobot-base.js'
const ratioColor = colorScale([10, 30, 50, 70])
-module.exports = class UptimeRobotRatio extends UptimeRobotBase {
- static get route() {
- return {
- base: 'uptimerobot/ratio',
- pattern: ':numberOfDays(\\d+)?/:monitorSpecificKey',
- }
+export default class UptimeRobotRatio extends UptimeRobotBase {
+ static route = {
+ base: 'uptimerobot/ratio',
+ pattern: ':numberOfDays(\\d+)?/:monitorSpecificKey',
}
- static get examples() {
- return [
- {
- title: 'Uptime Robot ratio (30 days)',
- pattern: ':monitorSpecificKey',
- namedParams: {
- monitorSpecificKey: 'm778918918-3e92c097147760ee39d02d36',
- },
- staticPreview: this.render({ ratio: 100 }),
+ static openApi = {
+ '/uptimerobot/ratio/{monitorSpecificKey}': {
+ get: {
+ summary: 'Uptime Robot ratio (30 days)',
+ parameters: pathParams({
+ name: 'monitorSpecificKey',
+ example: 'm778918918-3e92c097147760ee39d02d36',
+ }),
},
- {
- title: 'Uptime Robot ratio (7 days)',
- pattern: '7/:monitorSpecificKey',
- namedParams: {
- monitorSpecificKey: 'm778918918-3e92c097147760ee39d02d36',
- },
- staticPreview: this.render({ ratio: 100 }),
+ },
+ '/uptimerobot/ratio/7/{monitorSpecificKey}': {
+ get: {
+ summary: 'Uptime Robot ratio (7 days)',
+ parameters: pathParams({
+ name: 'monitorSpecificKey',
+ example: 'm778918918-3e92c097147760ee39d02d36',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'uptime',
- }
+ static defaultBadgeData = {
+ label: 'uptime',
}
static render({ ratio }) {
diff --git a/services/uptimerobot/uptimerobot-ratio.tester.js b/services/uptimerobot/uptimerobot-ratio.tester.js
index c3b9912be27ff..d50c077df141d 100644
--- a/services/uptimerobot/uptimerobot-ratio.tester.js
+++ b/services/uptimerobot/uptimerobot-ratio.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const { isPercentage } = require('../test-validators')
-const { invalidJSON } = require('../response-fixtures')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isPercentage } from '../test-validators.js'
+import { invalidJSON } from '../response-fixtures.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Uptime Robot: Percentage (valid)')
.get('/m778918918-3e92c097147760ee39d02d36.json')
@@ -34,7 +33,7 @@ t.create('Uptime Robot: Percentage (unspecified error)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(200, '{"stat": "fail"}')
+ .reply(200, '{"stat": "fail"}'),
)
.expectBadge({ label: 'uptime', message: 'service error' })
@@ -43,7 +42,7 @@ t.create('Uptime Robot: Percentage (service unavailable)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(503, '{"error": "oh noes!!"}')
+ .reply(503, '{"error": "oh noes!!"}'),
)
.expectBadge({ label: 'uptime', message: 'inaccessible' })
@@ -52,7 +51,7 @@ t.create('Uptime Robot: Percentage (unexpected response, valid json)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(200, '[]')
+ .reply(200, '[]'),
)
.expectBadge({ label: 'uptime', message: 'invalid response data' })
@@ -61,6 +60,6 @@ t.create('Uptime Robot: Percentage (unexpected response, invalid json)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(invalidJSON)
+ .reply(invalidJSON),
)
.expectBadge({ label: 'uptime', message: 'unparseable json response' })
diff --git a/services/uptimerobot/uptimerobot-status.service.js b/services/uptimerobot/uptimerobot-status.service.js
index 92921c8769a61..bfc881f763982 100644
--- a/services/uptimerobot/uptimerobot-status.service.js
+++ b/services/uptimerobot/uptimerobot-status.service.js
@@ -1,53 +1,73 @@
-'use strict'
+import { pathParam } from '../index.js'
+import { queryParamSchema, queryParams } from '../website-status.js'
+import UptimeRobotBase from './uptimerobot-base.js'
-const UptimeRobotBase = require('./uptimerobot-base')
-
-module.exports = class UptimeRobotStatus extends UptimeRobotBase {
- static get route() {
- return {
- base: 'uptimerobot/status',
- pattern: ':monitorSpecificKey',
- }
+export default class UptimeRobotStatus extends UptimeRobotBase {
+ static route = {
+ base: 'uptimerobot/status',
+ pattern: ':monitorSpecificKey',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'Uptime Robot status',
- namedParams: {
- monitorSpecificKey: 'm778918918-3e92c097147760ee39d02d36',
- },
- staticPreview: this.render({ status: 2 }),
+ static openApi = {
+ '/uptimerobot/status/{monitorSpecificKey}': {
+ get: {
+ summary: 'Uptime Robot status',
+ parameters: [
+ pathParam({
+ name: 'monitorSpecificKey',
+ example: 'm778918918-3e92c097147760ee39d02d36',
+ }),
+ ...queryParams,
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'status',
- }
+ static defaultBadgeData = {
+ label: 'status',
}
- static render({ status }) {
+ static render({
+ status,
+ upMessage = 'up',
+ downMessage = 'down',
+ upColor = 'brightgreen',
+ downColor = 'red',
+ }) {
switch (status) {
case 0:
return { message: 'paused', color: 'yellow' }
case 1:
return { message: 'not checked yet', color: 'yellowgreen' }
case 2:
- return { message: 'up', color: 'brightgreen' }
+ return { message: upMessage, color: upColor }
case 8:
return { message: 'seems down', color: 'orange' }
case 9:
- return { message: 'down', color: 'red' }
+ return { message: downMessage, color: downColor }
default:
throw Error('Should not get here due to validation')
}
}
- async handle({ monitorSpecificKey }) {
+ async handle(
+ { monitorSpecificKey },
+ {
+ up_message: upMessage,
+ down_message: downMessage,
+ up_color: upColor,
+ down_color: downColor,
+ },
+ ) {
const { monitors } = await this.fetch({ monitorSpecificKey })
const { status } = monitors[0]
- return this.constructor.render({ status })
+ return this.constructor.render({
+ status,
+ upMessage,
+ downMessage,
+ upColor,
+ downColor,
+ })
}
}
diff --git a/services/uptimerobot/uptimerobot-status.tester.js b/services/uptimerobot/uptimerobot-status.tester.js
index da6d6d1847ca0..1af85f668dc10 100644
--- a/services/uptimerobot/uptimerobot-status.tester.js
+++ b/services/uptimerobot/uptimerobot-status.tester.js
@@ -1,15 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { invalidJSON } = require('../response-fixtures')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { invalidJSON } from '../response-fixtures.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isUptimeStatus = Joi.string().valid(
'paused',
'not checked yet',
'up',
'seems down',
- 'down'
+ 'down',
)
t.create('Uptime Robot: Status (valid)')
@@ -35,7 +34,7 @@ t.create('Uptime Robot: Status (unspecified error)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(200, '{"stat": "fail"}')
+ .reply(200, '{"stat": "fail"}'),
)
.expectBadge({ label: 'status', message: 'service error' })
@@ -44,7 +43,7 @@ t.create('Uptime Robot: Status (service unavailable)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(503, '{"error": "oh noes!!"}')
+ .reply(503, '{"error": "oh noes!!"}'),
)
.expectBadge({ label: 'status', message: 'inaccessible' })
@@ -53,7 +52,7 @@ t.create('Uptime Robot: Status (unexpected response, valid json)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(200, '[]')
+ .reply(200, '[]'),
)
.expectBadge({ label: 'status', message: 'invalid response data' })
@@ -62,6 +61,6 @@ t.create('Uptime Robot: Status (unexpected response, invalid json)')
.intercept(nock =>
nock('https://api.uptimerobot.com')
.post('/v2/getMonitors')
- .reply(invalidJSON)
+ .reply(invalidJSON),
)
.expectBadge({ label: 'status', message: 'unparseable json response' })
diff --git a/services/vaadin-directory/vaadin-directory-base.js b/services/vaadin-directory/vaadin-directory-base.js
index 17776c985cce7..3bffe42a6f473 100644
--- a/services/vaadin-directory/vaadin-directory-base.js
+++ b/services/vaadin-directory/vaadin-directory-base.js
@@ -1,14 +1,10 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService } = require('..')
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService } from '../index.js'
const schema = Joi.object({
ratingCount: nonNegativeInteger,
- averageRating: Joi.number()
- .min(0)
- .required(),
+ averageRating: Joi.number().min(0).required(),
latestAvailableRelease: Joi.object({
publicationDate: Joi.date().required(),
name: Joi.string().required(),
@@ -20,9 +16,9 @@ class BaseVaadinDirectoryService extends BaseJsonService {
async fetch({ packageName }) {
return this._requestJson({
schema,
- url: `https://vaadin.com/vaadincom/directory-service/components/search/findByUrlIdentifier`,
+ url: 'https://vaadin.com/vaadincom/directory-service/components/search/findByUrlIdentifier',
options: {
- qs: {
+ searchParams: {
projection: 'summary',
urlIdentifier: packageName,
},
@@ -31,4 +27,4 @@ class BaseVaadinDirectoryService extends BaseJsonService {
}
}
-module.exports = { BaseVaadinDirectoryService }
+export { BaseVaadinDirectoryService }
diff --git a/services/vaadin-directory/vaadin-directory-rating-count.service.js b/services/vaadin-directory/vaadin-directory-rating-count.service.js
index 496143c1d4855..ffd450030ee13 100644
--- a/services/vaadin-directory/vaadin-directory-rating-count.service.js
+++ b/services/vaadin-directory/vaadin-directory-rating-count.service.js
@@ -1,35 +1,30 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { BaseVaadinDirectoryService } from './vaadin-directory-base.js'
-const { metric } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseVaadinDirectoryService } = require('./vaadin-directory-base')
+export default class VaadinDirectoryRatingCount extends BaseVaadinDirectoryService {
+ static category = 'rating'
-module.exports = class VaadinDirectoryRatingCount extends BaseVaadinDirectoryService {
- static get category() {
- return 'rating'
+ static route = {
+ base: 'vaadin-directory',
+ pattern: ':alias(rc|rating-count)/:packageName',
}
- static get route() {
- return {
- base: 'vaadin-directory',
- pattern: ':alias(rc|rating-count)/:packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Vaadin Directory',
- pattern: 'rating-count/:packageName',
- namedParams: { packageName: 'vaadinvaadin-grid' },
- staticPreview: this.render({ ratingCount: 6 }),
- keywords: ['vaadin-directory', 'rating-count'],
+ static openApi = {
+ '/vaadin-directory/rating-count/{packageName}': {
+ get: {
+ summary: 'Vaadin Directory Rating Count',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vaadinvaadin-grid',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'rating count' }
+ static defaultBadgeData = {
+ label: 'rating count',
}
static render({ ratingCount }) {
diff --git a/services/vaadin-directory/vaadin-directory-rating-count.tester.js b/services/vaadin-directory/vaadin-directory-rating-count.tester.js
index f6b602e757930..8b43ed993cb71 100644
--- a/services/vaadin-directory/vaadin-directory-rating-count.tester.js
+++ b/services/vaadin-directory/vaadin-directory-rating-count.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('rating count of component')
.get('/rating-count/vaadinvaadin-grid.json')
@@ -17,9 +16,7 @@ t.create('rating count of component')
message: Joi.string().regex(/^\d+?\stotal$/),
})
-t.create('not found')
- .get('/rating-count/does-not-exist.json')
- .expectBadge({
- label: 'rating count',
- message: 'not found',
- })
+t.create('not found').get('/rating-count/does-not-exist.json').expectBadge({
+ label: 'rating count',
+ message: 'not found',
+})
diff --git a/services/vaadin-directory/vaadin-directory-rating.service.js b/services/vaadin-directory/vaadin-directory-rating.service.js
index f1a6546fa4d40..846e7416558bd 100644
--- a/services/vaadin-directory/vaadin-directory-rating.service.js
+++ b/services/vaadin-directory/vaadin-directory-rating.service.js
@@ -1,35 +1,37 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { starRating } from '../text-formatters.js'
+import { floorCount as floorCountColor } from '../color-formatters.js'
+import { BaseVaadinDirectoryService } from './vaadin-directory-base.js'
-const { starRating } = require('../text-formatters')
-const { floorCount: floorCountColor } = require('../color-formatters')
-const { BaseVaadinDirectoryService } = require('./vaadin-directory-base')
+export default class VaadinDirectoryRating extends BaseVaadinDirectoryService {
+ static category = 'rating'
-module.exports = class VaadinDirectoryRating extends BaseVaadinDirectoryService {
- static get category() {
- return 'rating'
+ static route = {
+ base: 'vaadin-directory',
+ pattern: ':format(star|stars|rating)/:packageName',
}
- static get route() {
- return {
- base: 'vaadin-directory',
- pattern: ':format(star|stars|rating)/:packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Vaadin Directory',
- pattern: ':format(stars|rating)/:packageName',
- namedParams: { format: 'rating', packageName: 'vaadinvaadin-grid' },
- staticPreview: this.render({ format: 'rating', score: 4.75 }),
- keywords: ['vaadin-directory'],
+ static openApi = {
+ '/vaadin-directory/{format}/{packageName}': {
+ get: {
+ summary: 'Vaadin Directory Rating',
+ parameters: pathParams(
+ {
+ name: 'format',
+ example: 'rating',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ },
+ {
+ name: 'packageName',
+ example: 'vaadinvaadin-grid',
+ },
+ ),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'rating' }
+ static defaultBadgeData = {
+ label: 'rating',
}
static render({ format, score }) {
diff --git a/services/vaadin-directory/vaadin-directory-rating.tester.js b/services/vaadin-directory/vaadin-directory-rating.tester.js
index 48d762dfacb7d..e4074eb1e6430 100644
--- a/services/vaadin-directory/vaadin-directory-rating.tester.js
+++ b/services/vaadin-directory/vaadin-directory-rating.tester.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isStarRating } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { isStarRating } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('stars of component displayed in star icons')
.get('/star/vaadinvaadin-grid.json')
@@ -25,9 +24,7 @@ t.create('rating of the component (eg: 4.2/5)')
message: Joi.string().regex(/^\d\.\d\/5$/),
})
-t.create('not found')
- .get('/rating/does-not-exist.json')
- .expectBadge({
- label: 'rating',
- message: 'not found',
- })
+t.create('not found').get('/rating/does-not-exist.json').expectBadge({
+ label: 'rating',
+ message: 'not found',
+})
diff --git a/services/vaadin-directory/vaadin-directory-release-date.service.js b/services/vaadin-directory/vaadin-directory-release-date.service.js
index f8ed20866f6e0..6a435d19b25ba 100644
--- a/services/vaadin-directory/vaadin-directory-release-date.service.js
+++ b/services/vaadin-directory/vaadin-directory-release-date.service.js
@@ -1,45 +1,33 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderDateBadge } from '../date.js'
+import { BaseVaadinDirectoryService } from './vaadin-directory-base.js'
-const { formatDate } = require('../text-formatters')
-const { age: ageColor } = require('../color-formatters')
-const { BaseVaadinDirectoryService } = require('./vaadin-directory-base')
+export default class VaadinDirectoryReleaseDate extends BaseVaadinDirectoryService {
+ static category = 'activity'
-module.exports = class VaadinDirectoryReleaseDate extends BaseVaadinDirectoryService {
- static get category() {
- return 'activity'
+ static route = {
+ base: 'vaadin-directory',
+ pattern: ':alias(rd|release-date)/:packageName',
}
- static get route() {
- return {
- base: 'vaadin-directory',
- pattern: ':alias(rd|release-date)/:packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Vaadin Directory',
- pattern: 'release-date/:packageName',
- namedParams: { packageName: 'vaadinvaadin-grid' },
- staticPreview: this.render({ date: '2018-12-12' }),
- keywords: ['vaadin-directory', 'date', 'latest release date'],
+ static openApi = {
+ '/vaadin-directory/release-date/{packageName}': {
+ get: {
+ summary: 'Vaadin Directory Release Date',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vaadinvaadin-grid',
+ }),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'latest release date' }
+ },
}
- static render({ date }) {
- return { message: formatDate(date), color: ageColor(date) }
+ static defaultBadgeData = {
+ label: 'latest release date',
}
async handle({ alias, packageName }) {
const data = await this.fetch({ packageName })
- return this.constructor.render({
- date: data.latestAvailableRelease.publicationDate,
- })
+ return renderDateBadge(data.latestAvailableRelease.publicationDate)
}
}
diff --git a/services/vaadin-directory/vaadin-directory-release-date.tester.js b/services/vaadin-directory/vaadin-directory-release-date.tester.js
index f5102380ea68b..9c1c68dc64318 100644
--- a/services/vaadin-directory/vaadin-directory-release-date.tester.js
+++ b/services/vaadin-directory/vaadin-directory-release-date.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isFormattedDate } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('latest release date of the component (format: yyyy-mm-dd)')
.get('/rd/vaadinvaadin-grid.json')
diff --git a/services/vaadin-directory/vaadin-directory-status.service.js b/services/vaadin-directory/vaadin-directory-status.service.js
index 8e0521b88ada1..2ada00c6a1ff1 100644
--- a/services/vaadin-directory/vaadin-directory-status.service.js
+++ b/services/vaadin-directory/vaadin-directory-status.service.js
@@ -1,32 +1,28 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { BaseVaadinDirectoryService } from './vaadin-directory-base.js'
-const { BaseVaadinDirectoryService } = require('./vaadin-directory-base')
+export default class VaadinDirectoryStatus extends BaseVaadinDirectoryService {
+ static category = 'other'
-module.exports = class VaadinDirectoryStatus extends BaseVaadinDirectoryService {
- static get category() {
- return 'other'
+ static route = {
+ base: 'vaadin-directory/status',
+ pattern: ':packageName',
}
- static get route() {
- return {
- base: 'vaadin-directory/status',
- pattern: ':packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Vaadin Directory',
- namedParams: { packageName: 'vaadinvaadin-grid' },
- staticPreview: this.render({ status: 'published' }),
- keywords: ['vaadin-directory', 'status'],
+ static openApi = {
+ '/vaadin-directory/status/{packageName}': {
+ get: {
+ summary: 'Vaadin Directory Status',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vaadinvaadin-grid',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'vaadin directory' }
+ static defaultBadgeData = {
+ label: 'vaadin directory',
}
static render({ status }) {
diff --git a/services/vaadin-directory/vaadin-directory-status.tester.js b/services/vaadin-directory/vaadin-directory-status.tester.js
index 52f5773e1d47f..7caa48c2165fa 100644
--- a/services/vaadin-directory/vaadin-directory-status.tester.js
+++ b/services/vaadin-directory/vaadin-directory-status.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('publish status of the component')
.get('/vaadinvaadin-grid.json')
@@ -10,9 +9,7 @@ t.create('publish status of the component')
message: Joi.equal('published', 'unpublished'),
})
-t.create('not found')
- .get('/does-not-exist.json')
- .expectBadge({
- label: 'vaadin directory',
- message: 'not found',
- })
+t.create('not found').get('/does-not-exist.json').expectBadge({
+ label: 'vaadin directory',
+ message: 'not found',
+})
diff --git a/services/vaadin-directory/vaadin-directory-version.service.js b/services/vaadin-directory/vaadin-directory-version.service.js
index bb68f93a37f92..814e1a61a4b4b 100644
--- a/services/vaadin-directory/vaadin-directory-version.service.js
+++ b/services/vaadin-directory/vaadin-directory-version.service.js
@@ -1,34 +1,29 @@
-'use strict'
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseVaadinDirectoryService } from './vaadin-directory-base.js'
-const { renderVersionBadge } = require('../version')
-const { BaseVaadinDirectoryService } = require('./vaadin-directory-base')
+export default class VaadinDirectoryVersion extends BaseVaadinDirectoryService {
+ static category = 'version'
-module.exports = class VaadinDirectoryVersion extends BaseVaadinDirectoryService {
- static get category() {
- return 'version'
+ static route = {
+ base: 'vaadin-directory',
+ pattern: ':alias(v|version)/:packageName',
}
- static get route() {
- return {
- base: 'vaadin-directory',
- pattern: ':alias(v|version)/:packageName',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Vaadin Directory',
- pattern: 'v/:packageName',
- namedParams: { packageName: 'vaadinvaadin-grid' },
- staticPreview: renderVersionBadge({ version: 'v5.3.0-alpha4' }),
- keywords: ['vaadin-directory', 'latest'],
+ static openApi = {
+ '/vaadin-directory/v/{packageName}': {
+ get: {
+ summary: 'Vaadin Directory Version',
+ parameters: pathParams({
+ name: 'packageName',
+ example: 'vaadinvaadin-grid',
+ }),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return { label: 'vaadin directory' }
+ static defaultBadgeData = {
+ label: 'vaadin directory',
}
async handle({ alias, packageName }) {
diff --git a/services/vaadin-directory/vaadin-directory-version.tester.js b/services/vaadin-directory/vaadin-directory-version.tester.js
index e498041c09c17..957e6006a5959 100644
--- a/services/vaadin-directory/vaadin-directory-version.tester.js
+++ b/services/vaadin-directory/vaadin-directory-version.tester.js
@@ -1,7 +1,6 @@
-'use strict'
-
-const { isSemver } = require('../test-validators')
-const t = (module.exports = require('../tester').createServiceTester())
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('latest version of the component (can have v prefixed or without)')
.get('/v/vaadinvaadin-grid.json')
@@ -17,9 +16,7 @@ t.create('latest version of the component (can have v prefixed or without)')
message: isSemver,
})
-t.create('not found')
- .get('/v/does-not-exist.json')
- .expectBadge({
- label: 'vaadin directory',
- message: 'not found',
- })
+t.create('not found').get('/v/does-not-exist.json').expectBadge({
+ label: 'vaadin directory',
+ message: 'not found',
+})
diff --git a/services/validators.js b/services/validators.js
index 1da1625598fd6..4a195ff642407 100644
--- a/services/validators.js
+++ b/services/validators.js
@@ -1,36 +1,92 @@
-'use strict'
+/**
+ * This module contains commonly used validators.
+ *
+ * @module
+ */
-const { semver, semverRange } = require('joi-extension-semver')
-const Joi = require('@hapi/joi')
- .extend(semver)
- .extend(semverRange)
+import {
+ semver as joiSemver,
+ semverRange as joiSemverRange,
+} from 'joi-extension-semver'
+import joi from 'joi'
+const Joi = joi.extend(joiSemver).extend(joiSemverRange)
-const optionalNonNegativeInteger = Joi.number()
- .integer()
- .min(0)
+/**
+ * Joi validator that checks if a value is a number, an integer, and greater than or equal to zero.
+ *
+ * @type {Joi}
+ */
+const optionalNonNegativeInteger = Joi.number().integer().min(0)
-module.exports = {
- optionalNonNegativeInteger,
+export { optionalNonNegativeInteger }
- nonNegativeInteger: optionalNonNegativeInteger.required(),
+/**
+ * Joi validator that checks if a value is a number, an integer, greater than or equal to zero and the value must be present.
+ *
+ * @type {Joi}
+ */
+export const nonNegativeInteger = optionalNonNegativeInteger.required()
- anyInteger: Joi.number()
- .integer()
- .required(),
+/**
+ * Joi validator that checks if a value is a number, an integer and the value must be present.
+ *
+ * @type {Joi}
+ */
+export const anyInteger = Joi.number().integer().required()
- semver: Joi.semver()
- .valid()
- .required(),
+/**
+ * Joi validator that checks if a value is a valid semantic versioning string and the value must be present.
+ *
+ * @type {Joi}
+ */
+export const semver = Joi.semver().valid().required()
- semverRange: Joi.semverRange()
- .valid()
- .required(),
+/**
+ * Joi validator that checks if a value is a valid semantic versioning range and the value must be present.
+ *
+ * @type {Joi}
+ */
+export const semverRange = Joi.semverRange().valid().required()
- optionalDottedVersionNClausesWithOptionalSuffix: Joi.string().regex(
- /^\d+(\.\d+)*([-+].*)?$/
- ),
+/**
+ * Joi validator that checks if a value is a string that matches a regular expression.
+ * The regular expression matches strings that start with one or more digits, followed by zero or more groups of a dot and one or more digits,
+ * followed by an optional suffix that starts with a dash or a plus sign and can contain any characters.
+ * This validator can be used to validate properties that can be version strings with an optional suffix or absent.
+ * For example, some valid values for this validator are: 1, 1.0, 1.0.0, 1.0.0-beta
+ * Some invalid values for this validator are: abc, 1.a, 1.0-, .1
+ *
+ * @type {Joi}
+ */
+export const optionalDottedVersionNClausesWithOptionalSuffix =
+ Joi.string().regex(/^\d+(\.\d+)*([-+].*)?$/)
- // TODO This accepts URLs with query strings and fragments, which for some
- // purposes should be rejected.
- optionalUrl: Joi.string().uri({ scheme: ['http', 'https'] }),
-}
+/**
+ * Joi validator that checks if a value is a URL
+ *
+ * TODO: This accepts URLs with query strings and fragments, which for some purposes should be rejected.
+ *
+ * @type {Joi}
+ */
+export const optionalUrl = Joi.string().uri({ scheme: ['http', 'https'] })
+
+/**
+ * Joi validator that checks if a value is a URL and the value must be present.
+ *
+ * @type {Joi}
+ */
+export const url = optionalUrl.required()
+
+/**
+ * Joi validator for a file size in bytes (direct numeric value)
+ *
+ * @type {Joi}
+ */
+export const fileSizeBytes = Joi.number().integer().positive().required()
+
+/**
+ * Joi validator that checks if a value is a relative-only URI
+ *
+ * @type {Joi}
+ */
+export const relativeUri = Joi.string().uri({ relativeOnly: true })
diff --git a/services/vcpkg/vcpkg-version-helpers.js b/services/vcpkg/vcpkg-version-helpers.js
new file mode 100644
index 0000000000000..23cc2d7f4bb3c
--- /dev/null
+++ b/services/vcpkg/vcpkg-version-helpers.js
@@ -0,0 +1,17 @@
+import { InvalidResponse } from '../index.js'
+
+export function parseVersionFromVcpkgManifest(manifest) {
+ if (manifest['version-date']) {
+ return manifest['version-date']
+ }
+ if (manifest['version-semver']) {
+ return manifest['version-semver']
+ }
+ if (manifest['version-string']) {
+ return manifest['version-string']
+ }
+ if (manifest.version) {
+ return manifest.version
+ }
+ throw new InvalidResponse({ prettyMessage: 'missing version' })
+}
diff --git a/services/vcpkg/vcpkg-version-helpers.spec.js b/services/vcpkg/vcpkg-version-helpers.spec.js
new file mode 100644
index 0000000000000..c73e804b2ca1a
--- /dev/null
+++ b/services/vcpkg/vcpkg-version-helpers.spec.js
@@ -0,0 +1,41 @@
+import { expect } from 'chai'
+import { InvalidResponse } from '../index.js'
+import { parseVersionFromVcpkgManifest } from './vcpkg-version-helpers.js'
+
+describe('parseVersionFromVcpkgManifest', function () {
+ it('returns a version when `version` field is detected', function () {
+ expect(
+ parseVersionFromVcpkgManifest({
+ version: '2.12.1',
+ }),
+ ).to.equal('2.12.1')
+ })
+
+ it('returns a version when `version-date` field is detected', function () {
+ expect(
+ parseVersionFromVcpkgManifest({
+ 'version-date': '2022-12-04',
+ }),
+ ).to.equal('2022-12-04')
+ })
+
+ it('returns a version when `version-semver` field is detected', function () {
+ expect(
+ parseVersionFromVcpkgManifest({
+ 'version-semver': '3.11.2',
+ }),
+ ).to.equal('3.11.2')
+ })
+
+ it('returns a version when `version-date` field is detected', function () {
+ expect(
+ parseVersionFromVcpkgManifest({
+ 'version-string': '22.01',
+ }),
+ ).to.equal('22.01')
+ })
+
+ it('rejects when no version field variant is detected', function () {
+ expect(() => parseVersionFromVcpkgManifest('{}')).to.throw(InvalidResponse)
+ })
+})
diff --git a/services/vcpkg/vcpkg-version.service.js b/services/vcpkg/vcpkg-version.service.js
new file mode 100644
index 0000000000000..001c1b0a41839
--- /dev/null
+++ b/services/vcpkg/vcpkg-version.service.js
@@ -0,0 +1,70 @@
+import Joi from 'joi'
+import { ConditionalGithubAuthV3Service } from '../github/github-auth-service.js'
+import { fetchJsonFromRepo } from '../github/github-common-fetch.js'
+import { renderVersionBadge } from '../version.js'
+import { NotFound, pathParams } from '../index.js'
+import { parseVersionFromVcpkgManifest } from './vcpkg-version-helpers.js'
+
+// Handle the different version fields available in Vcpkg manifests
+// https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json?source=recommendations#version
+const vcpkgManifestSchema = Joi.alternatives()
+ .match('one')
+ .try(
+ Joi.object({
+ version: Joi.string().required(),
+ }).required(),
+ Joi.object({
+ 'version-date': Joi.string().required(),
+ }).required(),
+ Joi.object({
+ 'version-semver': Joi.string().required(),
+ }).required(),
+ Joi.object({
+ 'version-string': Joi.string().required(),
+ }).required(),
+ )
+
+export default class VcpkgVersion extends ConditionalGithubAuthV3Service {
+ static category = 'version'
+
+ static route = { base: 'vcpkg/v', pattern: ':portName' }
+
+ static openApi = {
+ '/vcpkg/v/{portName}': {
+ get: {
+ summary: 'Vcpkg Version',
+ parameters: pathParams({
+ name: 'portName',
+ example: 'entt',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'vcpkg' }
+
+ static render({ version }) {
+ return renderVersionBadge({ version })
+ }
+
+ async handle({ portName }) {
+ try {
+ const manifest = await fetchJsonFromRepo(this, {
+ schema: vcpkgManifestSchema,
+ user: 'microsoft',
+ repo: 'vcpkg',
+ branch: 'master',
+ filename: `ports/${portName}/vcpkg.json`,
+ })
+ const version = parseVersionFromVcpkgManifest(manifest)
+ return this.constructor.render({ version })
+ } catch (error) {
+ if (error instanceof NotFound) {
+ throw new NotFound({
+ prettyMessage: 'port not found',
+ })
+ }
+ throw error
+ }
+ }
+}
diff --git a/services/vcpkg/vcpkg-version.tester.js b/services/vcpkg/vcpkg-version.tester.js
new file mode 100644
index 0000000000000..b7ad6afc3e0a7
--- /dev/null
+++ b/services/vcpkg/vcpkg-version.tester.js
@@ -0,0 +1,16 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+t.create('gets nlohmann-json port version')
+ .get('/nlohmann-json.json')
+ .expectBadge({ label: 'vcpkg', color: 'blue', message: isSemver })
+
+t.create('gets not found error for invalid port')
+ .get('/this-port-does-not-exist.json')
+ .expectBadge({
+ label: 'vcpkg',
+ color: 'red',
+ message: 'port not found',
+ })
diff --git a/services/version.js b/services/version.js
index bd71881311ba8..5534b7a7a662c 100644
--- a/services/version.js
+++ b/services/version.js
@@ -2,15 +2,24 @@
* Utilities relating to generating badges relating to version numbers. Includes
* comparing versions to determine the latest, and determining the color to use
* for the badge based on whether the version is a stable release.
- *
* For utilities specific to PHP version ranges, see php-version.js.
+ *
+ * @module
*/
-'use strict'
-const semver = require('semver')
-const { addv } = require('./text-formatters')
-const { version: versionColor } = require('./color-formatters')
+import semver from 'semver'
+import { addv } from './text-formatters.js'
+import { version as versionColor } from './color-formatters.js'
+/**
+ * Compares two arrays of numbers lexicographically and returns an integer value.
+ *
+ * @param {number[]} a - The first array to compare
+ * @param {number[]} b - The second array to compare
+ * @returns {number} -1 if a is smaller than b, 1 if a is larger than b, 0 if a and b are equal
+ * @example
+ * listCompare([1, 2, 3], [1, 2, 4]) // returns -1 because the third element of the first array is smaller than the third element of the second array.
+ */
function listCompare(a, b) {
const alen = a.length
const blen = b.length
@@ -24,8 +33,15 @@ function listCompare(a, b) {
return alen - blen
}
-// Take string versions.
-// -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
+/**
+ * Compares two strings representing version numbers lexicographically and returns an integer value.
+ *
+ * @param {string} v1 - The first version to compare
+ * @param {string} v2 - The second version to compare
+ * @returns {number} -1 if v1 is smaller than v2, 1 if v1 is larger than v2, 0 if v1 and v2 are equal
+ * @example
+ * compareDottedVersion('1.2.3', '1.2.4') // returns -1 because numeric part of first version is smaller than the numeric part of second version.
+ */
function compareDottedVersion(v1, v2) {
const parts1 = /([0-9.]+)(.*)$/.exec(v1)
const parts2 = /([0-9.]+)(.*)$/.exec(v2)
@@ -43,15 +59,22 @@ function compareDottedVersion(v1, v2) {
return distinguisher1 < distinguisher2
? -1
: distinguisher1 > distinguisher2
- ? 1
- : 0
+ ? 1
+ : 0
}
}
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0
}
-// Take a list of string versions.
-// Return the latest, or undefined, if there are none.
+/**
+ * Finds the largest version number lexicographically from an array of strings representing version numbers and returns it as a string.
+ *
+ * @param {string[]} versions - The array of version numbers to compare
+ * @returns {string|undefined} The largest version number as a string, or undefined if the array is empty
+ * @example
+ * latestDottedVersion(['1.2.3', '1.2.4', '1.3', '2.0']) // returns '2.0' because it is the largest version number in the array.
+ * latestDottedVersion([]) // returns undefined because the array is empty.
+ */
function latestDottedVersion(versions) {
const len = versions.length
if (len === 0) {
@@ -66,6 +89,19 @@ function latestDottedVersion(versions) {
return version
}
+/**
+ * Finds the largest version number lexicographically or semantically from an array of strings representing version numbers and returns it as a string.
+ * latestMaybeSemVer() is used for versions that match some kind of dotted version pattern.
+ *
+ * @param {string[]} versions - The array of version numbers to compare
+ * @param {boolean} pre - Whether to include pre-release versions or not
+ * @returns {string|undefined} The largest version number as a string, or undefined if the array is empty
+ * @example
+ * latestMaybeSemVer(['1.2.3', '1.2.4', '1.3', '2.0'], false) // returns '2.0' because it is the largest version number and pre-release versions are excluded.
+ * latestMaybeSemVer(['1.2.3', '1.2.4', '1.3', '2.0'], true) // returns '2.0' because pre-release versions are included but none of them are present in the array.
+ * latestMaybeSemVer(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta'], false) // returns '1.2.4' because pre-release versions are excluded and it is the largest version number among the remaining ones.
+ * latestMaybeSemVer(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta'], true) // returns '2.0-beta' because pre-release versions are included and it is the largest version number.
+ */
function latestMaybeSemVer(versions, pre) {
let version = ''
@@ -76,20 +112,33 @@ function latestMaybeSemVer(versions, pre) {
try {
// coerce to string then lowercase otherwise alpha > RC
version = versions.sort((a, b) =>
- semver.rcompare(
+ semver.compareBuild(
`${a}`.toLowerCase(),
`${b}`.toLowerCase(),
- /* loose */ true
- )
- )[0]
+ /* loose */ true,
+ ),
+ )[versions.length - 1]
} catch (e) {
version = latestDottedVersion(versions)
}
return version
}
-// Given a list of versions (as strings), return the latest version.
-// Return undefined if no version could be found.
+/**
+ * Finds the largest version number lexicographically or semantically from an array of strings representing version numbers and returns it as a string.
+ * latest() is looser than latestMaybeSemVer() as it will attempt to make sense of anything, falling back to alphabetic sorting.
+ * We should ideally prefer latest() over latestMaybeSemVer() when adding version badges.
+ *
+ * @param {string[]} versions - The array of version numbers to compare
+ * @param {object} [options] - An optional object that contains additional options
+ * @param {boolean} [options.pre=false] - Whether to include pre-release versions or not, defaults to false
+ * @returns {string|undefined} The largest version number as a string, or undefined if the array is empty
+ * @example
+ * latest(['1.2.3', '1.2.4', '1.3', '2.0'], { pre: false }) // returns '2.0' because it is the largest version number and pre-release versions are excluded.
+ * latest(['1.2.3', '1.2.4', '1.3', '2.0'], { pre: true }) // returns '2.0' because pre-release versions are included but none of them are present in the array.
+ * latest(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta'], { pre: false }) // returns '1.2.4' because pre-release versions are excluded and it is the largest version number among the remaining ones.
+ * latest(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta'], { pre: true }) // returns '2.0-beta' because pre-release versions are included and it is the largest version number.
+ */
function latest(versions, { pre = false } = {}) {
let version = ''
let origVersions = versions
@@ -111,7 +160,7 @@ function latest(versions, { pre = false } = {}) {
// fall back to a case-insensitive string comparison
if (version == null) {
origVersions = origVersions.sort((a, b) =>
- a.toLowerCase().localeCompare(b.toLowerCase())
+ a.toLowerCase().localeCompare(b.toLowerCase()),
)
version = origVersions[origVersions.length - 1]
}
@@ -119,8 +168,17 @@ function latest(versions, { pre = false } = {}) {
return version
}
-// Slice the specified number of dotted parts from the given semver version.
-// e.g. slice('2.4.7', 'minor') -> '2.4'
+/**
+ * Slices the specified number of dotted parts from the given semver version.
+ *
+ * @param {string} v - The semver version to slice
+ * @param {string} releaseType - The release type to slice up to. Can be one of "major", "minor", or "patch"
+ * @returns {string|null} The sliced version as a string, or null if the version is not valid
+ * @example
+ * slice('2.4.7', 'minor') // returns '2.4' because it slices the version string up to the minor component.
+ * slice('2.4.7-alpha', 'patch') // returns '2.4.7-alpha' because it slices the version string up to the patch component and preserves the prerelease component.
+ * slice('2.4', 'patch') // returns null because the version string is not valid according to semver rules.
+ */
function slice(v, releaseType) {
if (!semver.valid(v, /* loose */ true)) {
return null
@@ -149,23 +207,59 @@ function slice(v, releaseType) {
}
}
+/**
+ * Returns the start of the range that matches a given version string.
+ *
+ * @param {string} v - A version string that follows the Semantic Versioning specification. The function will accept and coerce invalid versions into valid ones.
+ * @returns {string} The start of the range that matches the given version string, or null if no match is found.
+ * @throws {TypeError} If v is an invalid semver range
+ * @example
+ * rangeStart('^1.2.3') // returns '1.2.3'
+ * rangeStart('>=2.0.0') // returns '2.0.0'
+ * rangeStart('1.x || >=2.5.0 || 5.0.0 - 7.2.3') // returns '1.0.0'
+ * rangeStart('1.2.x') // returns '1.2.0'
+ * rangeStart('1.2.*') // returns '1.2.0-0'
+ * rangeStart(null) // throws TypeError: Invalid Version: null
+ * rangeStart('') // throws TypeError: Invalid Version:
+ */
function rangeStart(v) {
const range = new semver.Range(v, /* loose */ true)
return range.set[0][0].semver.version
}
-function renderVersionBadge({ version, tag, defaultLabel }) {
+/**
+ * Creates a badge object that displays information about a version number. It should usually be used to output a version badge.
+ *
+ * @param {object} options - An object that contains the options for the badge
+ * @param {string} options.version - The version number to display on the badge
+ * @param {string} [options.tag] - The tag to display on the badge, such as "alpha" or "beta"
+ * @param {string} [options.defaultLabel] - The default label to display on the badge, such as "npm" or "github"
+ * @param {string} [options.prefix] - The prefix to display on the message, such as ">=", "v", overrides the default behavior of using addv
+ * @param {string} [options.suffix] - The suffix to display on the message, such as "tested"
+ * @param {Function} [options.versionFormatter=versionColor] - The function to use to format the color of the badge based on the version number
+ * @param {boolean} [options.isPrerelease] - Whether the version is explicitly marked as a prerelease by upstream API
+ * @returns {object} A badge object that has three properties: label, message, and color
+ * @example
+ * renderVersionBadge({version: '1.2.3', tag: 'alpha', defaultLabel: 'npm'}) // returns {label: 'npm@alpha', message: 'v1.2.3', color: 'orange'} because
+ * it uses the tag and the defaultLabel to create the label, the addv function to add a 'v' prefix to the version in message,
+ * and the versionColor function to assign an orange color based on the version.
+ */
+function renderVersionBadge({
+ version,
+ tag,
+ defaultLabel,
+ prefix,
+ suffix,
+ versionFormatter = versionColor,
+ isPrerelease,
+}) {
return {
- label: tag ? `${defaultLabel}@${tag}` : undefined,
- message: addv(version),
- color: versionColor(version),
+ label: tag ? `${defaultLabel}@${tag}` : defaultLabel,
+ message:
+ (prefix ? `${prefix}${version}` : addv(version)) +
+ (suffix ? ` ${suffix}` : ''),
+ color: versionFormatter(isPrerelease ? 'pre' : version),
}
}
-module.exports = {
- latest,
- listCompare,
- slice,
- rangeStart,
- renderVersionBadge,
-}
+export { latest, listCompare, slice, rangeStart, renderVersionBadge }
diff --git a/services/version.spec.js b/services/version.spec.js
index 37ec4c5663919..90f87140bf824 100644
--- a/services/version.spec.js
+++ b/services/version.spec.js
@@ -1,10 +1,8 @@
-'use strict'
-
-const { test, given } = require('sazerac')
-const { latest, slice, rangeStart, renderVersionBadge } = require('./version')
+import { test, given } from 'sazerac'
+import { latest, slice, rangeStart, renderVersionBadge } from './version.js'
const includePre = true
-describe('Version helpers', function() {
+describe('Version helpers', function () {
test(latest, () => {
// semver-compatible versions.
given(['1.0.0', '1.0.2', '1.0.1']).expect('1.0.2')
@@ -56,7 +54,7 @@ describe('Version helpers', function() {
'v1.0.1-RC.2',
'v1.0.0',
],
- { pre: includePre }
+ { pre: includePre },
).expect('v1.0.1-RC.2')
given(
[
@@ -68,7 +66,7 @@ describe('Version helpers', function() {
'v1.0.1-RC.2',
'v1.0.1',
],
- { pre: includePre }
+ { pre: includePre },
).expect('v1.0.1')
given(
[
@@ -78,7 +76,7 @@ describe('Version helpers', function() {
'v1.0.1-beta.1',
'v1.0.1-RC.1',
],
- { pre: includePre }
+ { pre: includePre },
).expect('v1.0.1-RC.1')
// Exclude pre-releases
@@ -118,8 +116,11 @@ describe('Version helpers', function() {
// Semver mixed with non semver versions
given(['1.0.0', '1.0.2', '1.1', '1.0', 'notaversion2', '12bcde4']).expect(
- '1.1'
+ '1.1',
)
+
+ // build qualifiers - https://github.com/badges/shields/issues/4172
+ given(['0.3.9', '0.4.0+1', '0.4.0+9']).expect('0.4.0+9')
})
test(slice, () => {
@@ -149,5 +150,69 @@ describe('Version helpers', function() {
message: 'v1.2.3',
color: 'blue',
})
+ given({ version: '1.2.3', defaultLabel: 'npm' }).expect({
+ label: 'npm',
+ message: 'v1.2.3',
+ color: 'blue',
+ })
+ given({ version: '1.2.3', suffix: 'tested' }).expect({
+ label: undefined,
+ message: 'v1.2.3 tested',
+ color: 'blue',
+ })
+ given({
+ version: '1.2.3',
+ tag: 'beta',
+ defaultLabel: 'github',
+ suffix: 'tested',
+ }).expect({
+ label: 'github@beta',
+ message: 'v1.2.3 tested',
+ color: 'blue',
+ })
+ given({ version: '1.2.3', prefix: '^' }).expect({
+ label: undefined,
+ message: '^1.2.3',
+ color: 'blue',
+ })
+ given({
+ version: '1.2.3',
+ tag: 'alpha',
+ defaultLabel: 'npm',
+ prefix: '^',
+ }).expect({
+ label: 'npm@alpha',
+ message: '^1.2.3',
+ color: 'blue',
+ })
+ given({
+ version: '1.2.3',
+ defaultLabel: 'npm',
+ prefix: '^',
+ }).expect({
+ label: 'npm',
+ message: '^1.2.3',
+ color: 'blue',
+ })
+ given({
+ version: '1.2.3',
+ prefix: '^',
+ suffix: 'tested',
+ }).expect({
+ label: undefined,
+ message: '^1.2.3 tested',
+ color: 'blue',
+ })
+ given({
+ version: '1.2.3',
+ tag: 'beta',
+ defaultLabel: 'github',
+ prefix: '^',
+ suffix: 'tested',
+ }).expect({
+ label: 'github@beta',
+ message: '^1.2.3 tested',
+ color: 'blue',
+ })
})
})
diff --git a/services/versioneye/versioneye.service.js b/services/versioneye/versioneye.service.js
deleted file mode 100644
index 8fa1d3ce356b7..0000000000000
--- a/services/versioneye/versioneye.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- category: 'downloads',
- route: {
- base: 'versioneye/d',
- pattern: ':various+',
- },
- label: 'versioneye',
- dateAdded: new Date('2018-08-20'),
-})
diff --git a/services/versioneye/versioneye.tester.js b/services/versioneye/versioneye.tester.js
deleted file mode 100644
index 8523ffa54bfdc..0000000000000
--- a/services/versioneye/versioneye.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = new ServiceTester({ id: 'versioneye', title: 'VersionEye' })
-module.exports = t
-
-t.create('no longer available (previously dependencies status)')
- .get('/d/ruby/rails.json')
- .expectBadge({
- label: 'versioneye',
- message: 'no longer available',
- })
diff --git a/services/visual-studio-app-center/visual-studio-app-center-base.js b/services/visual-studio-app-center/visual-studio-app-center-base.js
deleted file mode 100644
index a4c10f75da757..0000000000000
--- a/services/visual-studio-app-center/visual-studio-app-center-base.js
+++ /dev/null
@@ -1,40 +0,0 @@
-'use strict'
-
-const { BaseJsonService } = require('..')
-
-const keywords = [
- 'visual-studio',
- 'vsac',
- 'visual-studio-app-center',
- 'app-center',
-]
-
-const documentation =
- "You will need to create a read-only API token here ."
-
-class BaseVisualStudioAppCenterService extends BaseJsonService {
- async fetch({
- owner,
- app,
- token,
- schema,
- url = `https://api.appcenter.ms/v0.1/apps/${owner}/${app}/releases/latest`,
- }) {
- return this._requestJson({
- schema,
- options: {
- headers: {
- 'X-API-Token': token,
- },
- },
- errorMessages: {
- 401: 'invalid token',
- 403: 'project not found',
- 404: 'project not found',
- },
- url,
- })
- }
-}
-
-module.exports = { keywords, documentation, BaseVisualStudioAppCenterService }
diff --git a/services/visual-studio-app-center/visual-studio-app-center-builds.service.js b/services/visual-studio-app-center/visual-studio-app-center-builds.service.js
index 0fd6a290b2c9d..5f4ca0935a331 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-builds.service.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-builds.service.js
@@ -1,62 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { NotFound } = require('..')
-const {
- BaseVisualStudioAppCenterService,
- keywords,
- documentation,
-} = require('./visual-studio-app-center-base')
-
-const schema = Joi.array().items({
- result: isBuildStatus.required(),
+import { retiredService } from '../index.js'
+
+// Visual Studio App Center was retired. See: https://learn.microsoft.com/en-us/appcenter/retirement
+const VisualStudioAppCenterBuilds = retiredService({
+ category: 'build',
+ route: {
+ base: 'visual-studio-app-center/builds',
+ pattern: ':owner/:app/:branch/:token',
+ },
+ label: 'visualstudioappcenter',
+ dateAdded: new Date('2025-08-30'),
})
-module.exports = class VisualStudioAppCenterBuilds extends BaseVisualStudioAppCenterService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'visual-studio-app-center/builds',
- pattern: ':owner/:app/:branch/:token',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio App Center Builds',
- namedParams: {
- owner: 'jct',
- app: 'my-amazing-app',
- branch: 'master',
- token: 'ac70cv...',
- },
- staticPreview: renderBuildStatusBadge({ status: 'succeeded' }),
- keywords,
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'build',
- }
- }
-
- async handle({ owner, app, branch, token }) {
- const json = await this.fetch({
- token,
- schema,
- url: `https://api.appcenter.ms/v0.1/apps/${owner}/${app}/branches/${branch}/builds`,
- })
- if (json[0] === undefined)
- // Fetch will return a 200 with no data if no builds were found.
- throw new NotFound({ prettyMessage: 'no builds found' })
- return renderBuildStatusBadge({ status: json[0].result })
- }
-}
+export default VisualStudioAppCenterBuilds
diff --git a/services/visual-studio-app-center/visual-studio-app-center-builds.tester.js b/services/visual-studio-app-center/visual-studio-app-center-builds.tester.js
index 062285493987c..d7a3d59a1822e 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-builds.tester.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-builds.tester.js
@@ -1,34 +1,26 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
+export const t = new ServiceTester({
+ id: 'VisualStudioAppCenterBuilds',
+ title: 'VisualStudioAppCenterBuilds',
+ pathPrefix: '/visual-studio-app-center/builds',
+})
-// Mocked response rather than real data as old builds are deleted after some time.
-t.create('Valid Build')
- .get('/nock/nock/master/token.json')
- .intercept(nock =>
- nock('https://api.appcenter.ms/v0.1/apps/')
- .get('/nock/nock/branches/master/builds')
- .reply(200, [
- {
- result: 'succeeded',
- },
- ])
- )
- .expectBadge({
- label: 'build',
- message: 'passing',
- })
+t.create('Valid Build').get('/nock/nock/master/token.json').expectBadge({
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
+})
t.create('Invalid Branch')
.get('/jct/test-1/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'build',
- message: 'no builds found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Invalid API Token')
.get('/jct/test-1/master/invalid.json')
.expectBadge({
- label: 'build',
- message: 'invalid token',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.service.js b/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.service.js
index af1dd7c8402c7..78cc73a2dc75a 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.service.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.service.js
@@ -1,61 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const {
- BaseVisualStudioAppCenterService,
- keywords,
- documentation,
-} = require('./visual-studio-app-center-base')
-
-const schema = Joi.object({
- app_os: Joi.string().required(),
- min_os: Joi.string().required(),
-}).required()
-
-module.exports = class VisualStudioAppCenterReleasesOSVersion extends BaseVisualStudioAppCenterService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'visual-studio-app-center/releases/osver',
- pattern: ':owner/:app/:token',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio App Center (Minimum) OS Version',
- namedParams: {
- owner: 'jct',
- app: 'my-amazing-app',
- token: 'ac70cv...',
- },
- staticPreview: this.render({ min_os: '4.1', app_os: 'Android' }),
- keywords,
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'min version',
- color: 'blue',
- }
- }
-
- static render({ app_os, min_os }) {
- return {
- label: `${app_os.toLowerCase()}`,
- message: `${min_os}+`,
- }
- }
-
- async handle({ owner, app, token }) {
- const { app_os, min_os } = await this.fetch({ owner, app, token, schema })
- return this.constructor.render({ app_os, min_os })
- }
-}
+import { retiredService } from '../index.js'
+
+// Visual Studio App Center was retired. See: https://learn.microsoft.com/en-us/appcenter/retirement
+const VisualStudioAppCenterReleasesOSVersion = retiredService({
+ category: 'version',
+ route: {
+ base: 'visual-studio-app-center/releases/osver',
+ pattern: ':owner/:app/:token',
+ },
+ label: 'visualstudioappcenter',
+ dateAdded: new Date('2025-08-30'),
+})
+
+export default VisualStudioAppCenterReleasesOSVersion
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.tester.js b/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.tester.js
index c8a2adf01da87..65c60217d6fff 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.tester.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-osversion.tester.js
@@ -1,38 +1,35 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Note:
-// Unfortunately an Invalid user, invalid project, valid API token test is not possible due to the way Microsoft cache their responses.
-// For this reason 404 and 403 will instead both display 'project not found'
+export const t = new ServiceTester({
+ id: 'VisualStudioAppCenterReleasesOSVersion',
+ title: 'VisualStudioAppCenterReleasesOSVersion',
+ pathPrefix: '/visual-studio-app-center/releases/osver',
+})
t.create('[fixed] Example Release')
- // This application will never have a new release created.
.get(
- '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json'
+ '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json',
)
.expectBadge({
- label: 'android',
- message: '4.1+',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Valid user, invalid project, valid API token')
.get('/jcx/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'min version',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Invalid user, invalid project, valid API token')
.get('/invalid/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'min version',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
-t.create('Invalid API Token')
- .get('/invalid/invalid/invalid.json')
- .expectBadge({
- label: 'min version',
- message: 'invalid token',
- })
+t.create('Invalid API Token').get('/invalid/invalid/invalid.json').expectBadge({
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
+})
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-size.service.js b/services/visual-studio-app-center/visual-studio-app-center-releases-size.service.js
index fde5401cbf2e1..831c14fc30b3b 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-size.service.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-size.service.js
@@ -1,61 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const prettyBytes = require('pretty-bytes')
-const { nonNegativeInteger } = require('../validators')
-const {
- BaseVisualStudioAppCenterService,
- keywords,
- documentation,
-} = require('./visual-studio-app-center-base')
-
-const schema = Joi.object({
- size: nonNegativeInteger,
-}).required()
-
-module.exports = class VisualStudioAppCenterReleasesSize extends BaseVisualStudioAppCenterService {
- static get category() {
- return 'size'
- }
-
- static get route() {
- return {
- base: 'visual-studio-app-center/releases/size',
- pattern: ':owner/:app/:token',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio App Center Size',
- namedParams: {
- owner: 'jct',
- app: 'my-amazing-app',
- token: 'ac70cv...',
- },
- staticPreview: this.render({ size: 8368844 }),
- keywords,
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'size',
- color: 'blue',
- }
- }
-
- static render({ size }) {
- return {
- message: prettyBytes(size),
- }
- }
-
- async handle({ owner, app, token }) {
- const { size } = await this.fetch({ owner, app, token, schema })
- return this.constructor.render({ size })
- }
-}
+import { retiredService } from '../index.js'
+
+// Visual Studio App Center was retired. See: https://learn.microsoft.com/en-us/appcenter/retirement
+const VisualStudioAppCenterReleasesSize = retiredService({
+ category: 'size',
+ route: {
+ base: 'visual-studio-app-center/releases/size',
+ pattern: ':owner/:app/:token',
+ },
+ label: 'visualstudioappcenter',
+ dateAdded: new Date('2025-08-30'),
+})
+
+export default VisualStudioAppCenterReleasesSize
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-size.tester.js b/services/visual-studio-app-center/visual-studio-app-center-releases-size.tester.js
index be651b227f9d9..dbf5eefddff12 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-size.tester.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-size.tester.js
@@ -1,48 +1,42 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
-const { isFileSize } = require('../test-validators')
+export const t = new ServiceTester({
+ id: 'VisualStudioAppCenterReleasesSize',
+ title: 'VisualStudioAppCenterReleasesSize',
+ pathPrefix: '/visual-studio-app-center/releases/size',
+})
t.create('8368844 bytes to 8.37 megabytes')
.get('/nock/nock/nock.json')
- .intercept(nock =>
- nock('https://api.appcenter.ms/v0.1/apps/')
- .get('/nock/nock/releases/latest')
- .reply(200, {
- size: 8368844,
- })
- )
.expectBadge({
- label: 'size',
- message: '8.37 MB',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Valid Release')
.get(
- '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json'
+ '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json',
)
.expectBadge({
- label: 'size',
- message: isFileSize,
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Valid user, invalid project, valid API token')
.get('/jcx/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'size',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Invalid user, invalid project, valid API token')
.get('/invalid/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'size',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
-t.create('Invalid API Token')
- .get('/invalid/invalid/invalid.json')
- .expectBadge({
- label: 'size',
- message: 'invalid token',
- })
+t.create('Invalid API Token').get('/invalid/invalid/invalid.json').expectBadge({
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
+})
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-version.service.js b/services/visual-studio-app-center/visual-studio-app-center-releases-version.service.js
index 9f6f067864e43..ccfb5fcd5bc47 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-version.service.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-version.service.js
@@ -1,61 +1,14 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { renderVersionBadge } = require('../version')
-const {
- BaseVisualStudioAppCenterService,
- keywords,
- documentation,
-} = require('./visual-studio-app-center-base')
-
-const schema = Joi.object({
- version: Joi.string().required(),
- short_version: Joi.string().required(),
-}).required()
-
-module.exports = class VisualStudioAppCenterReleasesVersion extends BaseVisualStudioAppCenterService {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: 'visual-studio-app-center/releases/version',
- pattern: ':owner/:app/:token',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio App Center Releases',
- namedParams: {
- owner: 'jct',
- app: 'my-amazing-app',
- token: 'ac70cv...',
- },
- staticPreview: renderVersionBadge({ version: '1.0 (4)' }),
- keywords,
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'release',
- }
- }
-
- async handle({ owner, app, token }) {
- const { version, short_version } = await this.fetch({
- owner,
- app,
- token,
- schema,
- })
- return renderVersionBadge({
- version: `${short_version} (${version})`,
- })
- }
-}
+import { retiredService } from '../index.js'
+
+// Visual Studio App Center was retired. See: https://learn.microsoft.com/en-us/appcenter/retirement
+const VisualStudioAppCenterReleasesVersion = retiredService({
+ category: 'version',
+ route: {
+ base: 'visual-studio-app-center/releases/version',
+ pattern: ':owner/:app/:token',
+ },
+ label: 'visualstudioappcenter',
+ dateAdded: new Date('2025-08-30'),
+})
+
+export default VisualStudioAppCenterReleasesVersion
diff --git a/services/visual-studio-app-center/visual-studio-app-center-releases-version.tester.js b/services/visual-studio-app-center/visual-studio-app-center-releases-version.tester.js
index 15932ef87c60f..063693233e2e1 100644
--- a/services/visual-studio-app-center/visual-studio-app-center-releases-version.tester.js
+++ b/services/visual-studio-app-center/visual-studio-app-center-releases-version.tester.js
@@ -1,38 +1,40 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const t = (module.exports = require('../tester').createServiceTester())
-
-// Note:
-// Unfortunately an Invalid user, invalid project, valid API token test is not possible due to the way Microsoft cache their responses.
-// For this reason 404 and 403 will instead both display 'project not found'
+export const t = new ServiceTester({
+ id: 'VisualStudioAppCenterReleasesVersion',
+ title: 'VisualStudioAppCenterReleasesVersion',
+ pathPrefix: '/visual-studio-app-center/releases/version',
+})
t.create('[fixed] Example Release')
- // This application will never have a new release created.
.get(
- '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json'
+ '/jct/test-fixed-android-react/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json',
)
.expectBadge({
- label: 'release',
- message: 'v1.0 (1)',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Valid user, invalid project, valid API token')
.get('/jcx/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'release',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
t.create('Invalid user, invalid project, valid API token')
.get('/invalid/invalid/8c9b519a0750095b9fea3d40b2645d8a0c24a2f3.json')
.expectBadge({
- label: 'release',
- message: 'project not found',
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
})
-t.create('Invalid API Token')
- .get('/invalid/invalid/invalid.json')
- .expectBadge({
- label: 'release',
- message: 'invalid token',
- })
+t.create('Missing Short Version').get('/nock/nock/nock.json').expectBadge({
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
+})
+
+t.create('Invalid API Token').get('/invalid/invalid/invalid.json').expectBadge({
+ label: 'visualstudioappcenter',
+ message: 'retired badge',
+})
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js
deleted file mode 100644
index 76cdf576d7a99..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.service.js
+++ /dev/null
@@ -1,71 +0,0 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const VisualStudioMarketplaceBase = require('./visual-studio-marketplace-base')
-
-const documentation = `
-
- This badge can show total installs, installs for Azure DevOps Services,
- or on-premises installs for Azure DevOps Server.
-
-`
-
-// This service exists separately from the other Marketplace downloads badges (in ./visual-studio-marketplace-downloads.js)
-// due differences in how the Marketplace tracks metrics for Azure DevOps extensions vs. other extension types.
-// See https://github.com/badges/shields/pull/2748 for more information on the discussion and decision.
-module.exports = class VisualStudioMarketplaceAzureDevOpsInstalls extends VisualStudioMarketplaceBase {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: 'visual-studio-marketplace/azure-devops/installs',
- pattern: ':measure(total|onprem|services)/:extensionId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio Marketplace Installs - Azure DevOps Extension',
- namedParams: {
- measure: 'total',
- extensionId: 'swellaby.mirror-git-repository',
- },
- staticPreview: this.render({ count: 651 }),
- keywords: this.keywords,
- documentation,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'installs',
- }
- }
-
- static render({ count }) {
- return {
- message: metric(count),
- color: downloadCount(count),
- }
- }
-
- async handle({ measure, extensionId }) {
- const json = await this.fetch({ extensionId })
- const { statistics } = this.transformStatistics({ json })
-
- if (measure === 'total') {
- return this.constructor.render({
- count: statistics.onpremDownloads + statistics.install,
- })
- } else if (measure === 'services') {
- return this.constructor.render({ count: statistics.install })
- } else {
- return this.constructor.render({ count: statistics.onpremDownloads })
- }
- }
-}
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.tester.js b/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.tester.js
deleted file mode 100644
index 1e770815d9c39..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-azure-devops-installs.tester.js
+++ /dev/null
@@ -1,125 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
-
-const mockResponse = {
- results: [
- {
- extensions: [
- {
- statistics: [
- {
- statisticName: 'install',
- value: 21,
- },
- {
- statisticName: 'onpremDownloads',
- value: 7,
- },
- ],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
-}
-
-t.create('Azure DevOps Extension total installs')
- .get('/total/swellaby.mirror-git-repository.json')
- .expectBadge({
- label: 'installs',
- message: isMetric,
- })
-
-t.create('Azure DevOps Extension services installs')
- .get('/services/swellaby.mirror-git-repository.json')
- .expectBadge({
- label: 'installs',
- message: isMetric,
- })
-
-t.create('invalid extension id')
- .get('/services/badges-shields.json')
- .expectBadge({
- label: 'installs',
- message: 'invalid extension id',
- })
-
-t.create('non existent extension')
- .get('/total/badges.shields-io-fake.json')
- .expectBadge({
- label: 'installs',
- message: 'extension not found',
- })
-
-t.create('total installs')
- .get('/total/swellaby.cobertura-transform.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, mockResponse)
- )
- .expectBadge({
- label: 'installs',
- message: '28',
- color: 'yellowgreen',
- })
-
-t.create('services installs')
- .get('/services/swellaby.cobertura-transform.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, mockResponse)
- )
- .expectBadge({
- label: 'installs',
- message: '21',
- color: 'yellowgreen',
- })
-
-t.create('onprem installs')
- .get('/onprem/swellaby.cobertura-transform.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, mockResponse)
- )
- .expectBadge({
- label: 'installs',
- message: '7',
- color: 'yellow',
- })
-
-t.create('zero installs')
- .get('/total/swellaby.cobertura-transform.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'installs',
- message: '0',
- color: 'red',
- })
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-base.js b/services/visual-studio-marketplace/visual-studio-marketplace-base.js
deleted file mode 100644
index c197ba333d990..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-base.js
+++ /dev/null
@@ -1,115 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const validate = require('../../core/base-service/validate')
-const { BaseJsonService, NotFound } = require('..')
-
-const extensionQuerySchema = Joi.object({
- results: Joi.array()
- .items(
- Joi.object({
- extensions: Joi.array()
- .items(
- Joi.object({
- statistics: Joi.array()
- .items(
- Joi.object({
- statisticName: Joi.string().required(),
- value: Joi.number().required(),
- })
- )
- .required(),
- versions: Joi.array()
- .items(
- Joi.object({
- version: Joi.string().regex(/^(\d+\.\d+\.\d+)(\.\d+)?$/),
- })
- )
- .min(1)
- .required(),
- })
- )
- .required(),
- })
- )
- .required(),
-}).required()
-
-const statisticSchema = Joi.object().keys({
- install: Joi.number().default(0),
- updateCount: Joi.number().default(0),
- onpremDownloads: Joi.number().default(0),
- averagerating: Joi.number().default(0),
- ratingcount: Joi.number().default(0),
-})
-
-module.exports = class VisualStudioMarketplaceBase extends BaseJsonService {
- static get keywords() {
- return [
- 'vscode',
- 'tfs',
- 'vsts',
- 'visual-studio-marketplace',
- 'vs-marketplace',
- 'vscode-marketplace',
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'vs marketplace',
- color: 'blue',
- }
- }
-
- async fetch({ extensionId }) {
- const url =
- 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery/'
- const body = {
- filters: [
- {
- criteria: [{ filterType: 7, value: extensionId }],
- },
- ],
- flags: 914,
- }
- const options = {
- method: 'POST',
- headers: {
- accept: 'application/json;api-version=3.0-preview.1',
- 'content-type': 'application/json',
- },
- body: JSON.stringify(body),
- }
-
- return this._requestJson({
- schema: extensionQuerySchema,
- url,
- options,
- errorMessages: {
- 400: 'invalid extension id',
- },
- })
- }
-
- transformExtension({ json }) {
- const extensions = json.results[0].extensions
- if (extensions.length === 0) {
- throw new NotFound({ prettyMessage: 'extension not found' })
- }
- return { extension: extensions[0] }
- }
-
- transformStatistics({ json }) {
- const { extension } = this.transformExtension({ json })
- const statistics = {}
-
- extension.statistics.forEach(({ statisticName, value }) => {
- statistics[statisticName] = value
- })
-
- const value = validate({ ErrorClass: Error }, statistics, statisticSchema)
-
- return { statistics: value }
- }
-}
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-downloads.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-downloads.service.js
deleted file mode 100644
index 921670c8404fb..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-downloads.service.js
+++ /dev/null
@@ -1,69 +0,0 @@
-'use strict'
-
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const VisualStudioMarketplaceBase = require('./visual-studio-marketplace-base')
-
-const documentation = `
-
- This is for Visual Studio and Visual Studio Code Extensions.
-
-
- For correct results on Azure DevOps Extensions, use the Azure DevOps Installs badge instead.
-
-`
-
-module.exports = class VisualStudioMarketplaceDownloads extends VisualStudioMarketplaceBase {
- static get category() {
- return 'downloads'
- }
-
- static get route() {
- return {
- base: '',
- pattern:
- '(visual-studio-marketplace|vscode-marketplace)/:measure(d|i)/:extensionId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio Marketplace Installs',
- pattern: 'visual-studio-marketplace/i/:extensionId',
- namedParams: { extensionId: 'ritwickdey.LiveServer' },
- staticPreview: this.render({ measure: 'i', count: 843 }),
- keywords: this.keywords,
- documentation,
- },
- {
- title: 'Visual Studio Marketplace Downloads',
- pattern: 'visual-studio-marketplace/d/:extensionId',
- namedParams: { extensionId: 'ritwickdey.LiveServer' },
- staticPreview: this.render({ measure: 'd', count: 1239 }),
- keywords: this.keywords,
- documentation,
- },
- ]
- }
-
- static render({ measure, count }) {
- const label = measure === 'd' ? 'downloads' : 'installs'
-
- return {
- label,
- message: metric(count),
- color: downloadCount(count),
- }
- }
-
- async handle({ measure, extensionId }) {
- const json = await this.fetch({ extensionId })
- const { statistics } = this.transformStatistics({ json })
- const count =
- measure === 'i'
- ? statistics.install
- : statistics.install + statistics.updateCount
- return this.constructor.render({ measure, count })
- }
-}
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-downloads.tester.js b/services/visual-studio-marketplace/visual-studio-marketplace-downloads.tester.js
deleted file mode 100644
index 39ddd38deada9..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-downloads.tester.js
+++ /dev/null
@@ -1,126 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { isMetric } = require('../test-validators')
-
-const mockResponse = {
- results: [
- {
- extensions: [
- {
- statistics: [
- {
- statisticName: 'install',
- value: 3,
- },
- {
- statisticName: 'updateCount',
- value: 7,
- },
- ],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
-}
-
-t.create('installs')
- .get('/visual-studio-marketplace/i/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'installs',
- message: isMetric,
- })
-
-t.create('downloads')
- .get('/visual-studio-marketplace/d/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
-
-t.create('invalid extension id')
- .get('/visual-studio-marketplace/d/badges-shields.json')
- .expectBadge({
- label: 'vs marketplace',
- message: 'invalid extension id',
- })
-
-t.create('non existent extension')
- .get('/visual-studio-marketplace/d/badges.shields-io-fake.json')
- .expectBadge({
- label: 'vs marketplace',
- message: 'extension not found',
- })
-
-t.create('installs')
- .get('/visual-studio-marketplace/i/swellaby.rust-pack.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, mockResponse)
- )
- .expectBadge({
- label: 'installs',
- message: '3',
- color: 'yellow',
- })
-
-t.create('zero installs')
- .get('/visual-studio-marketplace/i/swellaby.rust-pack.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'installs',
- message: '0',
- color: 'red',
- })
-
-t.create('downloads')
- .get('/visual-studio-marketplace/d/swellaby.rust-pack.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, mockResponse)
- )
- .expectBadge({
- label: 'downloads',
- message: '10',
- color: 'yellowgreen',
- })
-
-t.create('installs (legacy)')
- .get('/vscode-marketplace/i/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'installs',
- message: isMetric,
- })
-
-t.create('downloads (legacy)')
- .get('/vscode-marketplace/d/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'downloads',
- message: isMetric,
- })
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-rating.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-rating.service.js
deleted file mode 100644
index 8e457c7a36dc9..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-rating.service.js
+++ /dev/null
@@ -1,72 +0,0 @@
-'use strict'
-
-const { starRating } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const VisualStudioMarketplaceBase = require('./visual-studio-marketplace-base')
-
-module.exports = class VisualStudioMarketplaceRating extends VisualStudioMarketplaceBase {
- static get category() {
- return 'rating'
- }
-
- static get route() {
- return {
- base: '',
- pattern:
- '(visual-studio-marketplace|vscode-marketplace)/:format(r|stars)/:extensionId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio Marketplace Rating',
- pattern: 'visual-studio-marketplace/r/:extensionId',
- namedParams: { extensionId: 'ritwickdey.LiveServer' },
- staticPreview: this.render({
- format: 'r',
- averageRating: 4.79,
- ratingCount: 145,
- }),
- keywords: this.keywords,
- },
- {
- title: 'Visual Studio Marketplace Rating (Stars)',
- pattern: 'visual-studio-marketplace/stars/:extensionId',
- namedParams: { extensionId: 'ritwickdey.LiveServer' },
- staticPreview: this.render({
- format: 'stars',
- averageRating: 4.5,
- }),
- keywords: this.keywords,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'rating',
- }
- }
-
- static render({ format, averageRating, ratingCount }) {
- const message =
- format === 'r'
- ? `${averageRating.toFixed(1)}/5 (${ratingCount})`
- : starRating(averageRating)
- return {
- message,
- color: floorCount(averageRating, 2, 3, 4),
- }
- }
-
- async handle({ format, extensionId }) {
- const json = await this.fetch({ extensionId })
- const { statistics } = this.transformStatistics({ json })
- return this.constructor.render({
- format,
- averageRating: statistics.averagerating,
- ratingCount: statistics.ratingcount,
- })
- }
-}
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-rating.tester.js b/services/visual-studio-marketplace/visual-studio-marketplace-rating.tester.js
deleted file mode 100644
index d8847a42ae2f9..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-rating.tester.js
+++ /dev/null
@@ -1,136 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { withRegex, isStarRating } = require('../test-validators')
-
-const isVscodeRating = withRegex(/[0-5]\.[0-9]{1}\/5?\s*\([0-9]*\)$/)
-
-t.create('rating')
- .get('/visual-studio-marketplace/r/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'rating',
- message: isVscodeRating,
- })
-
-t.create('stars')
- .get('/visual-studio-marketplace/stars/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'rating',
- message: isStarRating,
- })
-
-t.create('rating')
- .get('/visual-studio-marketplace/r/ritwickdey.LiveServer.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [
- {
- statisticName: 'averagerating',
- value: 2.5,
- },
- {
- statisticName: 'ratingcount',
- value: 10,
- },
- ],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'rating',
- message: '2.5/5 (10)',
- color: 'yellowgreen',
- })
-
-t.create('zero rating')
- .get('/visual-studio-marketplace/r/ritwickdey.LiveServer.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'rating',
- message: '0.0/5 (0)',
- color: 'red',
- })
-
-t.create('stars')
- .get('/visual-studio-marketplace/stars/ritwickdey.LiveServer.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [
- {
- statisticName: 'averagerating',
- value: 4.7,
- },
- {
- statisticName: 'ratingcount',
- value: 200,
- },
- ],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'rating',
- message: '★★★★¾',
- color: 'brightgreen',
- })
-
-t.create('rating (legacy)')
- .get('/vscode-marketplace/r/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'rating',
- message: isVscodeRating,
- })
-
-t.create('stars (legacy)')
- .get('/vscode-marketplace/stars/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'rating',
- message: isStarRating,
- })
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js b/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js
deleted file mode 100644
index a42fdc52f995b..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-version.service.js
+++ /dev/null
@@ -1,52 +0,0 @@
-'use strict'
-
-const { renderVersionBadge } = require('../version')
-const VisualStudioMarketplaceBase = require('./visual-studio-marketplace-base')
-
-module.exports = class VisualStudioMarketplaceVersion extends VisualStudioMarketplaceBase {
- static get category() {
- return 'version'
- }
-
- static get route() {
- return {
- base: '',
- pattern: '(visual-studio-marketplace|vscode-marketplace)/v/:extensionId',
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Visual Studio Marketplace Version',
- pattern: 'visual-studio-marketplace/v/:extensionId',
- namedParams: { extensionId: 'swellaby.rust-pack' },
- staticPreview: this.render({ version: '0.2.7' }),
- keywords: this.keywords,
- },
- ]
- }
-
- static get defaultBadgeData() {
- return {
- label: 'version',
- }
- }
-
- static render({ version }) {
- return renderVersionBadge({ version })
- }
-
- transform({ json }) {
- const { extension } = this.transformExtension({ json })
- const version = extension.versions[0].version
- return { version }
- }
-
- async handle({ extensionId }) {
- const json = await this.fetch({ extensionId })
- const { version } = this.transform({ json })
-
- return this.constructor.render({ version })
- }
-}
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace-version.tester.js b/services/visual-studio-marketplace/visual-studio-marketplace-version.tester.js
deleted file mode 100644
index 2d1db265e52ec..0000000000000
--- a/services/visual-studio-marketplace/visual-studio-marketplace-version.tester.js
+++ /dev/null
@@ -1,76 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { withRegex } = require('../test-validators')
-
-const isMarketplaceVersion = withRegex(/^v(\d+\.\d+\.\d+)(\.\d+)?$/)
-
-t.create('rating')
- .get('/visual-studio-marketplace/v/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'version',
- message: isMarketplaceVersion,
- })
-
-t.create('version')
- .get('/visual-studio-marketplace/v/ritwickdey.LiveServer.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [],
- versions: [
- {
- version: '1.0.0',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'version',
- message: 'v1.0.0',
- color: 'blue',
- })
-
-t.create('pre-release version')
- .get('/visual-studio-marketplace/v/swellaby.vscode-rust-test-adapter.json')
- .intercept(nock =>
- nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
- .post(`/extensionquery/`)
- .reply(200, {
- results: [
- {
- extensions: [
- {
- statistics: [],
- versions: [
- {
- version: '0.3.8',
- },
- ],
- },
- ],
- },
- ],
- })
- )
- .expectBadge({
- label: 'version',
- message: 'v0.3.8',
- color: 'orange',
- })
-
-t.create('version (legacy)')
- .get('/vscode-marketplace/v/ritwickdey.LiveServer.json')
- .expectBadge({
- label: 'version',
- message: isMarketplaceVersion,
- })
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace.service.js b/services/visual-studio-marketplace/visual-studio-marketplace.service.js
new file mode 100644
index 0000000000000..13dde92c1911e
--- /dev/null
+++ b/services/visual-studio-marketplace/visual-studio-marketplace.service.js
@@ -0,0 +1,107 @@
+import { retiredService } from '../index.js'
+
+const dateAdded = new Date('2026-04-09')
+const label = 'visual-studio-marketplace'
+const pattern = ':various+'
+
+export default [
+ // Downloads / Installs
+ retiredService({
+ category: 'downloads',
+ route: { base: 'visual-studio-marketplace/d', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'downloads',
+ route: { base: 'visual-studio-marketplace/i', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'downloads',
+ route: { base: 'vscode-marketplace/d', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'downloads',
+ route: { base: 'vscode-marketplace/i', pattern },
+ label,
+ dateAdded,
+ }),
+
+ // Version
+ retiredService({
+ category: 'version',
+ route: { base: 'visual-studio-marketplace/v', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'version',
+ route: { base: 'vscode-marketplace/v', pattern },
+ label,
+ dateAdded,
+ }),
+
+ // Rating / Stars
+ retiredService({
+ category: 'rating',
+ route: { base: 'visual-studio-marketplace/r', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'rating',
+ route: { base: 'visual-studio-marketplace/stars', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'rating',
+ route: { base: 'vscode-marketplace/r', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'rating',
+ route: { base: 'vscode-marketplace/stars', pattern },
+ label,
+ dateAdded,
+ }),
+
+ // Release date / Last updated
+ retiredService({
+ category: 'activity',
+ route: { base: 'visual-studio-marketplace/release-date', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'activity',
+ route: { base: 'vscode-marketplace/release-date', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'activity',
+ route: { base: 'visual-studio-marketplace/last-updated', pattern },
+ label,
+ dateAdded,
+ }),
+ retiredService({
+ category: 'activity',
+ route: { base: 'vscode-marketplace/last-updated', pattern },
+ label,
+ dateAdded,
+ }),
+
+ // Azure DevOps installs (covers measure/extensionId segments)
+ retiredService({
+ category: 'downloads',
+ route: { base: 'visual-studio-marketplace/azure-devops/installs', pattern },
+ label,
+ dateAdded,
+ }),
+]
diff --git a/services/visual-studio-marketplace/visual-studio-marketplace.tester.js b/services/visual-studio-marketplace/visual-studio-marketplace.tester.js
new file mode 100644
index 0000000000000..7f7d70d5fe89a
--- /dev/null
+++ b/services/visual-studio-marketplace/visual-studio-marketplace.tester.js
@@ -0,0 +1,134 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'visualstudiomarketplace',
+ title: 'Visual Studio Marketplace (retired)',
+ pathPrefix: '/visual-studio-marketplace',
+})
+
+export const tLegacy = new ServiceTester({
+ id: 'visualstudiomarketplacelegacy',
+ title: 'VSCode Marketplace (retired)',
+ pathPrefix: '/vscode-marketplace',
+})
+
+// Downloads / Installs (visual-studio-marketplace)
+t.create('retired badge (visual-studio-marketplace downloads)')
+ .get('/d/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+t.create('retired badge (visual-studio-marketplace installs)')
+ .get('/i/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Downloads / Installs (vscode-marketplace - legacy)
+tLegacy
+ .create('retired badge (vscode-marketplace downloads)')
+ .get('/d/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+tLegacy
+ .create('retired badge (vscode-marketplace installs)')
+ .get('/i/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Version (visual-studio-marketplace)
+t.create('retired badge (version)')
+ .get('/v/lextudio.restructuredtext.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Version (vscode-marketplace - legacy)
+tLegacy
+ .create('retired badge (version legacy)')
+ .get('/v/lextudio.restructuredtext.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Rating / Stars (visual-studio-marketplace)
+t.create('retired badge (rating)')
+ .get('/r/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+t.create('retired badge (stars)')
+ .get('/stars/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Rating / Stars (vscode-marketplace - legacy)
+tLegacy
+ .create('retired badge (rating legacy)')
+ .get('/r/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+tLegacy
+ .create('retired badge (stars legacy)')
+ .get('/stars/ritwickdey.LiveServer.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Release date / Last updated (visual-studio-marketplace)
+t.create('retired badge (release date)')
+ .get('/release-date/yasht.terminal-all-in-one.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+t.create('retired badge (last updated)')
+ .get('/last-updated/yasht.terminal-all-in-one.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Release date / Last updated (vscode-marketplace - legacy)
+tLegacy
+ .create('retired badge (release date legacy)')
+ .get('/release-date/yasht.terminal-all-in-one.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+tLegacy
+ .create('retired badge (last updated legacy)')
+ .get('/last-updated/yasht.terminal-all-in-one.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
+
+// Azure DevOps installs (visual-studio-marketplace)
+t.create('retired badge (azure devops installs total)')
+ .get('/azure-devops/installs/total/swellaby.mirror-git-repository.json')
+ .expectBadge({
+ label: 'visual-studio-marketplace',
+ message: 'retired badge',
+ })
diff --git a/services/voxelshop/voxelshop-base.js b/services/voxelshop/voxelshop-base.js
new file mode 100644
index 0000000000000..18a956f9b1d31
--- /dev/null
+++ b/services/voxelshop/voxelshop-base.js
@@ -0,0 +1,50 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+
+const resourceSchema = Joi.object({
+ response: Joi.object({
+ resource: Joi.object({
+ downloads: Joi.number().required(),
+ reviews: Joi.object({
+ count: Joi.number().required(),
+ stars: Joi.number().required(),
+ }).required(),
+ updates: Joi.object({
+ latest: Joi.object({
+ version: Joi.string().required(),
+ }).required(),
+ }).required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const notFoundResourceSchema = Joi.object({
+ response: Joi.object({
+ success: Joi.boolean().required(),
+ errors: Joi.object().required(),
+ }).required(),
+})
+
+const resourceFoundOrNotSchema = Joi.alternatives(
+ resourceSchema,
+ notFoundResourceSchema,
+)
+
+const description = `
+You can find your resource ID in the url for your product page.
+Example: https://voxel.shop/product/323/polymart-plugin - Here the Resource ID is 323.
`
+
+class BaseVoxelShopService extends BaseJsonService {
+ async fetch({
+ resourceId,
+ schema = resourceFoundOrNotSchema,
+ url = `https://api.voxel.shop/v1/getResourceInfo/?resource_id=${resourceId}`,
+ }) {
+ return this._requestJson({
+ schema,
+ url,
+ })
+ }
+}
+
+export { description, BaseVoxelShopService }
diff --git a/services/voxelshop/voxelshop-downloads.service.js b/services/voxelshop/voxelshop-downloads.service.js
new file mode 100644
index 0000000000000..19ce6108a869f
--- /dev/null
+++ b/services/voxelshop/voxelshop-downloads.service.js
@@ -0,0 +1,38 @@
+import { pathParams } from '../index.js'
+import { NotFound } from '../../core/base-service/errors.js'
+import { renderDownloadsBadge } from '../downloads.js'
+import { BaseVoxelShopService, description } from './voxelshop-base.js'
+
+export default class VoxelShopDownloads extends BaseVoxelShopService {
+ static category = 'downloads'
+
+ static route = {
+ base: 'voxel-shop/dt',
+ pattern: ':resourceId',
+ }
+
+ static openApi = {
+ '/voxel-shop/dt/{resourceId}': {
+ get: {
+ summary: 'Voxel Shop Downloads',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '323',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'downloads',
+ }
+
+ async handle({ resourceId }) {
+ const { response } = await this.fetch({ resourceId })
+ if (!response.resource) {
+ throw new NotFound()
+ }
+ return renderDownloadsBadge({ downloads: response.resource.downloads })
+ }
+}
diff --git a/services/voxelshop/voxelshop-downloads.tester.js b/services/voxelshop/voxelshop-downloads.tester.js
new file mode 100644
index 0000000000000..9a239ad06b21b
--- /dev/null
+++ b/services/voxelshop/voxelshop-downloads.tester.js
@@ -0,0 +1,13 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Voxel Shop Plugin (id 323)').get('/323.json').expectBadge({
+ label: 'downloads',
+ message: isMetric,
+})
+
+t.create('Invalid Resource (id 0)').get('/0.json').expectBadge({
+ label: 'downloads',
+ message: 'not found',
+})
diff --git a/services/voxelshop/voxelshop-latest-version.service.js b/services/voxelshop/voxelshop-latest-version.service.js
new file mode 100644
index 0000000000000..87eff746e5e46
--- /dev/null
+++ b/services/voxelshop/voxelshop-latest-version.service.js
@@ -0,0 +1,40 @@
+import { pathParams } from '../index.js'
+import { NotFound } from '../../core/base-service/errors.js'
+import { renderVersionBadge } from '../version.js'
+import { BaseVoxelShopService, description } from './voxelshop-base.js'
+
+export default class VoxelShopLatestVersion extends BaseVoxelShopService {
+ static category = 'version'
+
+ static route = {
+ base: 'voxel-shop/v',
+ pattern: ':resourceId',
+ }
+
+ static openApi = {
+ '/voxel-shop/v/{resourceId}': {
+ get: {
+ summary: 'Voxel Shop Version',
+ description,
+ parameters: pathParams({
+ name: 'resourceId',
+ example: '323',
+ }),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'voxel.shop',
+ }
+
+ async handle({ resourceId }) {
+ const { response } = await this.fetch({ resourceId })
+ if (!response.resource) {
+ throw new NotFound()
+ }
+ return renderVersionBadge({
+ version: response.resource.updates.latest.version,
+ })
+ }
+}
diff --git a/services/voxelshop/voxelshop-latest-version.tester.js b/services/voxelshop/voxelshop-latest-version.tester.js
new file mode 100644
index 0000000000000..fc2d0639bb820
--- /dev/null
+++ b/services/voxelshop/voxelshop-latest-version.tester.js
@@ -0,0 +1,13 @@
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Voxel Shop Plugin (id 323)').get('/323.json').expectBadge({
+ label: 'voxel.shop',
+ message: isVPlusDottedVersionNClauses,
+})
+
+t.create('Invalid Resource (id 0)').get('/0.json').expectBadge({
+ label: 'voxel.shop',
+ message: 'not found',
+})
diff --git a/services/voxelshop/voxelshop-rating.service.js b/services/voxelshop/voxelshop-rating.service.js
new file mode 100644
index 0000000000000..63807279ca461
--- /dev/null
+++ b/services/voxelshop/voxelshop-rating.service.js
@@ -0,0 +1,61 @@
+import { pathParams } from '../index.js'
+import { starRating, metric } from '../text-formatters.js'
+import { floorCount } from '../color-formatters.js'
+import { NotFound } from '../../core/base-service/errors.js'
+import { BaseVoxelShopService, description } from './voxelshop-base.js'
+
+export default class VoxelShopRatings extends BaseVoxelShopService {
+ static category = 'rating'
+
+ static route = {
+ base: 'voxel-shop',
+ pattern: ':format(rating|stars)/:resourceId',
+ }
+
+ static openApi = {
+ '/voxel-shop/{format}/{resourceId}': {
+ get: {
+ summary: 'Voxel Shop Rating',
+ description,
+ parameters: pathParams(
+ {
+ name: 'format',
+ example: 'rating',
+ schema: { type: 'string', enum: this.getEnum('format') },
+ },
+ {
+ name: 'resourceId',
+ example: '323',
+ },
+ ),
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'rating',
+ }
+
+ static render({ format, total, average }) {
+ const message =
+ format === 'stars'
+ ? starRating(average)
+ : `${average}/5 (${metric(total)})`
+ return {
+ message,
+ color: floorCount(average, 2, 3, 4),
+ }
+ }
+
+ async handle({ format, resourceId }) {
+ const { response } = await this.fetch({ resourceId })
+ if (!response.resource) {
+ throw new NotFound()
+ }
+ return this.constructor.render({
+ format,
+ total: response.resource.reviews.count,
+ average: response.resource.reviews.stars.toFixed(2),
+ })
+ }
+}
diff --git a/services/voxelshop/voxelshop-rating.tester.js b/services/voxelshop/voxelshop-rating.tester.js
new file mode 100644
index 0000000000000..05e26a67734e0
--- /dev/null
+++ b/services/voxelshop/voxelshop-rating.tester.js
@@ -0,0 +1,27 @@
+import { isStarRating, withRegex } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('Stars - Voxel Shop Plugin (id 323)')
+ .get('/stars/323.json')
+ .expectBadge({
+ label: 'rating',
+ message: isStarRating,
+ })
+
+t.create('Stars - Invalid Resource (id 0)').get('/stars/0.json').expectBadge({
+ label: 'rating',
+ message: 'not found',
+})
+
+t.create('Rating - Voxel Shop Plugin (id 323)')
+ .get('/rating/323.json')
+ .expectBadge({
+ label: 'rating',
+ message: withRegex(/^(\d*\.\d+)(\/5 \()(\d+)(\))$/),
+ })
+
+t.create('Rating - Invalid Resource (id 0)').get('/rating/0.json').expectBadge({
+ label: 'rating',
+ message: 'not found',
+})
diff --git a/services/vpm/vpm-version.service.js b/services/vpm/vpm-version.service.js
new file mode 100644
index 0000000000000..b71bc73a849ca
--- /dev/null
+++ b/services/vpm/vpm-version.service.js
@@ -0,0 +1,80 @@
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { latest, renderVersionBadge } from '../version.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
+
+const queryParamSchema = Joi.object({
+ repository_url: url,
+ include_prereleases: Joi.equal(''),
+}).required()
+
+const schema = Joi.object({
+ packages: Joi.object()
+ .pattern(
+ /./,
+ Joi.object({
+ versions: Joi.object().pattern(/./, Joi.object()).min(1).required(),
+ }).required(),
+ )
+ .required(),
+}).required()
+
+export default class VpmVersion extends BaseJsonService {
+ static category = 'version'
+
+ static route = {
+ base: 'vpm/v',
+ pattern: ':packageId',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/vpm/v/{packageId}': {
+ get: {
+ summary: 'VPM Package Version',
+ description: 'VPM is the VRChat Package Manager',
+ parameters: [
+ pathParam({
+ name: 'packageId',
+ example: 'com.vrchat.udonsharp',
+ }),
+ queryParam({
+ name: 'repository_url',
+ example: 'https://packages.vrchat.com/curated?download',
+ required: true,
+ }),
+ queryParam({
+ name: 'include_prereleases',
+ schema: { type: 'boolean' },
+ example: null,
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'vpm',
+ }
+
+ async fetch({ repositoryUrl }) {
+ return this._requestJson({
+ schema,
+ url: repositoryUrl,
+ })
+ }
+
+ async handle(
+ { packageId },
+ { repository_url: repositoryUrl, include_prereleases: prereleases },
+ ) {
+ const data = await this.fetch({ repositoryUrl })
+ const pkg = data.packages[packageId]
+ if (pkg === undefined)
+ throw new NotFound({ prettyMessage: 'package not found' })
+ const versions = Object.keys(pkg.versions)
+ const version = latest(versions, { pre: prereleases !== undefined })
+
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/vpm/vpm-version.tester.js b/services/vpm/vpm-version.tester.js
new file mode 100644
index 0000000000000..eb9827e67d589
--- /dev/null
+++ b/services/vpm/vpm-version.tester.js
@@ -0,0 +1,51 @@
+import { isSemver } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('gets the package version of com.vrchat.udonsharp')
+ .get(
+ '/com.vrchat.udonsharp.json?repository_url=https%3A%2F%2Fpackages.vrchat.com%2Fcurated%3Fdownload',
+ )
+ .expectBadge({ label: 'vpm', message: isSemver })
+
+t.create('gets the latest version')
+ .intercept(nock =>
+ nock('https://packages.vrchat.com')
+ .get('/curated?download')
+ .reply(200, {
+ packages: {
+ 'com.vrchat.udonsharp': {
+ versions: {
+ '2.0.0': {},
+ '2.1.0-rc1': {},
+ '1.9.0': {},
+ },
+ },
+ },
+ }),
+ )
+ .get(
+ '/com.vrchat.udonsharp.json?repository_url=https%3A%2F%2Fpackages.vrchat.com%2Fcurated%3Fdownload',
+ )
+ .expectBadge({ label: 'vpm', message: 'v2.0.0' })
+
+t.create('gets the latest version including prerelease')
+ .intercept(nock =>
+ nock('https://packages.vrchat.com')
+ .get('/curated?download')
+ .reply(200, {
+ packages: {
+ 'com.vrchat.udonsharp': {
+ versions: {
+ '2.0.0': {},
+ '2.1.0-rc1': {},
+ '1.9.0': {},
+ },
+ },
+ },
+ }),
+ )
+ .get(
+ '/com.vrchat.udonsharp.json?repository_url=https%3A%2F%2Fpackages.vrchat.com%2Fcurated%3Fdownload&include_prereleases',
+ )
+ .expectBadge({ label: 'vpm', message: 'v2.1.0-rc1' })
diff --git a/services/w3c/w3c-validation-helper.js b/services/w3c/w3c-validation-helper.js
index 3f0a75f4016db..5880ac3faf18c 100644
--- a/services/w3c/w3c-validation-helper.js
+++ b/services/w3c/w3c-validation-helper.js
@@ -1,5 +1,3 @@
-'use strict'
-
const html5Expression =
'^HTML\\s?,\\s?SVG\\s?1\\.1\\s?,\\s?MathML\\s?3\\.0(\\s?,\\s?((ITS\\s?2\\.0)|(RDFa\\s?Lite\\s?1\\.1)))?$'
const html4Expression =
@@ -10,7 +8,7 @@ const svgExpression =
'^SVG\\s?1\\.1\\s?,\\s?URL\\s?,\\s?XHTML\\s?,\\s?MathML\\s?3\\.0$'
const presetRegex = new RegExp(
`(${html5Expression})|(${html4Expression})|(${xhtmlExpression})|(${svgExpression})`,
- 'i'
+ 'i',
)
const getMessage = messageTypes => {
@@ -22,7 +20,7 @@ const getMessage = messageTypes => {
}
const messages = messageTypeKeys.map(
- key => `${messageTypes[key]} ${key}${messageTypes[key] > 1 ? 's' : ''}`
+ key => `${messageTypes[key]} ${key}${messageTypes[key] > 1 ? 's' : ''}`,
)
return messages.join(', ')
}
@@ -79,78 +77,19 @@ const getSchema = preset => {
return schema.map(url => encodeURI(url)).join(' ')
}
-const documentation = `
-
-
- The W3C validation badge performs validation of the HTML, SVG, MathML, ITS, RDFa Lite, XHTML documents.
- The badge uses the type property of each message found in the messages from the validation results to determine to be an error or warning.
- The rules are as follows:
-
- info: These messages are counted as warnings
- error: These messages are counted as errors
- non-document-error: These messages are counted as errors
-
-
-
- This badge relies on the https://validator.nu/ service to perform the validation. Please refer to https://about.validator.nu/ for the full documentation and Terms of service.
- The following are required from the consumer for the badge to function.
+const description = `
+The W3C validation badge performs validation of the HTML, SVG, MathML, ITS, RDFa Lite, XHTML documents.
+The badge uses the type property of each message found in the messages from the validation results to determine to be an error or warning.
+The rules are as follows:
-
-
- Path:
-
-
- parser: The parser that is used for validation. This is a passthru value to the service
-
- default (This will not pass a parser to the API and make the API choose the parser based on the validated content)
- html (HTML)
- xml (XML; don’t load external entities)
- xmldtd (XML; load external entities)
-
-
-
-
-
- Query string:
-
-
- targetUrl (Required): This is the path for the document to be validated
-
-
- preset (Optional can be left as blank): This is used to determine the schema for the document to be valdiated against.
- The following are the allowed values
-
- HTML, SVG 1.1, MathML 3.0
- HTML, SVG 1.1, MathML 3.0, ITS 2.0
- HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1
- HTML 4.01 Strict, URL / XHTML 1.0 Strict, URL
- HTML 4.01 Transitional, URL / XHTML 1.0 Transitional, URL
- HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL
- XHTML, SVG 1.1, MathML 3.0
- XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1
- XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0
- SVG 1.1, URL, XHTML, MathML 3.0
-
-
-
-
-
-
+
+ info: These messages are counted as warnings
+ error: These messages are counted as errors
+ non-document-error: These messages are counted as errors
+
+
+This badge relies on the https://validator.nu/ service to perform the validation.
+Please refer to https://about.validator.nu/ for the full documentation and Terms of service.
`
-module.exports = {
- documentation,
- presetRegex,
- getColor,
- getMessage,
- getSchema,
-}
+export { description, presetRegex, getColor, getMessage, getSchema }
diff --git a/services/w3c/w3c-validation-helper.spec.js b/services/w3c/w3c-validation-helper.spec.js
index b5c98ed85283f..516b5f644a5de 100644
--- a/services/w3c/w3c-validation-helper.spec.js
+++ b/services/w3c/w3c-validation-helper.spec.js
@@ -1,15 +1,14 @@
-'use strict'
-const { expect } = require('chai')
-const { test, given, forCases } = require('sazerac')
-const {
+import { expect } from 'chai'
+import { test, given, forCases } from 'sazerac'
+import {
presetRegex,
getMessage,
getColor,
getSchema,
-} = require('./w3c-validation-helper')
+} from './w3c-validation-helper.js'
-describe('w3c-validation-helper', function() {
- describe('presetRegex', function() {
+describe('w3c-validation-helper', function () {
+ describe('presetRegex', function () {
function testing(preset) {
return presetRegex.test(preset)
}
@@ -61,8 +60,8 @@ describe('w3c-validation-helper', function() {
})
})
- describe('getColor', function() {
- it('returns "brightgreen" if no messages are provided', function() {
+ describe('getColor', function () {
+ it('returns "brightgreen" if no messages are provided', function () {
const messageTypes = {}
const actualResult = getColor(messageTypes)
@@ -70,7 +69,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('brightgreen')
})
- it('returns "yellow" if only warning messages are provided', function() {
+ it('returns "yellow" if only warning messages are provided', function () {
const messageTypes = { warning: 1 }
const actualResult = getColor(messageTypes)
@@ -78,7 +77,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('yellow')
})
- it('returns "red" if only error messages are provided', function() {
+ it('returns "red" if only error messages are provided', function () {
const messageTypes = { error: 1 }
const actualResult = getColor(messageTypes)
@@ -86,7 +85,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('red')
})
- it('returns "red" if both warning and error messages are provided', function() {
+ it('returns "red" if both warning and error messages are provided', function () {
const messageTypes = { warning: 3, error: 4 }
const actualResult = getColor(messageTypes)
@@ -95,8 +94,8 @@ describe('w3c-validation-helper', function() {
})
})
- describe('getMessage', function() {
- it('returns "validate" if no messages are provided', function() {
+ describe('getMessage', function () {
+ it('returns "validate" if no messages are provided', function () {
const messageTypes = {}
const actualResult = getMessage(messageTypes)
@@ -104,7 +103,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('validated')
})
- it('returns "1 error" if 1 error message is provided', function() {
+ it('returns "1 error" if 1 error message is provided', function () {
const messageTypes = { error: 1 }
const actualResult = getMessage(messageTypes)
@@ -112,7 +111,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('1 error')
})
- it('returns "2 errors" if 2 error messages are provided', function() {
+ it('returns "2 errors" if 2 error messages are provided', function () {
const messageTypes = { error: 2 }
const actualResult = getMessage(messageTypes)
@@ -120,7 +119,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('2 errors')
})
- it('returns "1 warning" if 1 warning message is provided', function() {
+ it('returns "1 warning" if 1 warning message is provided', function () {
const messageTypes = { warning: 1 }
const actualResult = getMessage(messageTypes)
@@ -128,7 +127,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('1 warning')
})
- it('returns "2 warnings" if 2 warning messages are provided', function() {
+ it('returns "2 warnings" if 2 warning messages are provided', function () {
const messageTypes = { warning: 2 }
const actualResult = getMessage(messageTypes)
@@ -136,7 +135,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('2 warnings')
})
- it('returns "1 error, 1 warning" if 1 error and 1 warning message is provided', function() {
+ it('returns "1 error, 1 warning" if 1 error and 1 warning message is provided', function () {
const messageTypes = { warning: 1, error: 1 }
const actualResult = getMessage(messageTypes)
@@ -144,7 +143,7 @@ describe('w3c-validation-helper', function() {
expect(actualResult).to.equal('1 error, 1 warning')
})
- it('returns "2 errors, 2 warnings" if 2 error and 2 warning message is provided', function() {
+ it('returns "2 errors, 2 warnings" if 2 error and 2 warning message is provided', function () {
const messageTypes = { error: 2, warning: 2 }
const actualResult = getMessage(messageTypes)
@@ -153,7 +152,7 @@ describe('w3c-validation-helper', function() {
})
})
- describe('getSchema', function() {
+ describe('getSchema', function () {
function execution(preset) {
return getSchema(preset)
}
@@ -162,103 +161,103 @@ describe('w3c-validation-helper', function() {
forCases([given(undefined), given(null), given('')]).expect(undefined)
})
- it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0" preset', function() {
+ it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0" preset', function () {
const preset = 'HTML,SVG 1.1,MathML 3.0'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/html5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/html5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
- it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0,ITS 2.0" preset', function() {
+ it('returns 3 schemas associated to the "HTML,SVG 1.1,MathML 3.0,ITS 2.0" preset', function () {
const preset = 'HTML,SVG 1.1,MathML 3.0,ITS 2.0'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/html5-its.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/html5-its.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
- it('returns 3 schemas associated to the "HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function() {
+ it('returns 3 schemas associated to the "HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function () {
const preset = 'HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/html5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/html5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
- it('returns 3 schemas associated to the "HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL" preset', function() {
+ it('returns 3 schemas associated to the "HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL" preset', function () {
const preset = 'HTML 4.01 Strict, URL/ XHTML 1.0 Strict, URL'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml10/xhtml-strict.rnc http://c.validator.nu/all-html4/'
+ 'http://s.validator.nu/xhtml10/xhtml-strict.rnc http://c.validator.nu/all-html4/',
)
})
- it('returns 3 schemas associated to the "HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL" preset', function() {
+ it('returns 3 schemas associated to the "HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL" preset', function () {
const preset = 'HTML 4.01 Transitional, URL/ XHTML 1.0 Transitional, URL'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://c.validator.nu/all-html4/'
+ 'http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://c.validator.nu/all-html4/',
)
})
- it('returns 3 schemas associated to the "HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL" preset', function() {
+ it('returns 3 schemas associated to the "HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL" preset', function () {
const preset = 'HTML 4.01 Frameset, URL/ XHTML 1.0 Frameset, URL'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml10/xhtml-frameset.rnc http://c.validator.nu/all-html4/'
+ 'http://s.validator.nu/xhtml10/xhtml-frameset.rnc http://c.validator.nu/all-html4/',
)
})
- it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0" preset', function() {
+ it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0" preset', function () {
const preset = 'XHTML, SVG 1.1, MathML 3.0'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/xhtml5.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
- it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function() {
+ it('returns 3 schemas associated to the "XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1" preset', function () {
const preset = 'XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/xhtml5-rdfalite.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
- it('returns 3 schemas associated to the "XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0" preset', function() {
+ it('returns 3 schemas associated to the "XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0" preset', function () {
const preset = 'XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/xhtml1-ruby-rdf-svg-mathml.rnc http://c.validator.nu/all-html4/'
+ 'http://s.validator.nu/xhtml1-ruby-rdf-svg-mathml.rnc http://c.validator.nu/all-html4/',
)
})
- it('returns 3 schemas associated to the "SVG 1.1, URL, XHTML, MathML 3.0" preset', function() {
+ it('returns 3 schemas associated to the "SVG 1.1, URL, XHTML, MathML 3.0" preset', function () {
const preset = 'SVG 1.1, URL, XHTML, MathML 3.0'
const actualResult = getSchema(preset)
expect(actualResult).to.equal(
- 'http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/'
+ 'http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
)
})
})
diff --git a/services/w3c/w3c-validation.service.js b/services/w3c/w3c-validation.service.js
index 8a467336fd271..e781efb4c4d53 100644
--- a/services/w3c/w3c-validation.service.js
+++ b/services/w3c/w3c-validation.service.js
@@ -1,14 +1,13 @@
-'use strict'
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
-const {
- documentation,
+import Joi from 'joi'
+import { url } from '../validators.js'
+import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
+import {
+ description,
presetRegex,
getColor,
getMessage,
getSchema,
-} = require('./w3c-validation-helper')
+} from './w3c-validation-helper.js'
const schema = Joi.object({
url: Joi.string().optional(),
@@ -17,53 +16,81 @@ const schema = Joi.object({
.items(
Joi.object({
type: Joi.string()
- .allow('info', 'error', 'non-document-error')
+ .equal('info', 'error', 'non-document-error')
.required(),
subType: Joi.string().optional(),
message: Joi.string().required(),
- })
+ }),
),
}).required()
const queryParamSchema = Joi.object({
- targetUrl: optionalUrl.required(),
- preset: Joi.string()
- .regex(presetRegex)
- .allow(''),
+ targetUrl: url,
+ preset: Joi.string().regex(presetRegex).allow(''),
}).required()
-module.exports = class W3cValidation extends BaseJsonService {
- static get category() {
- return 'analysis'
- }
+const parserDescription = `The parser that is used for validation. This is a passthru value to the service
+- \`default\`: This will not pass a parser to the API and make the API choose the parser based on the validated content
+- \`html\`: HTML
+- \`xml\`: XML (don't load external entities)
+- \`xmldtd\`: XML (load external entities)
+`
- static get route() {
- return {
- base: 'w3c-validation',
- pattern: ':parser(default|html|xml|xmldtd)',
- queryParamSchema,
- }
+export default class W3cValidation extends BaseJsonService {
+ static category = 'analysis'
+
+ static route = {
+ base: 'w3c-validation',
+ pattern: ':parser(default|html|xml|xmldtd)',
+ queryParamSchema,
}
- static get examples() {
- return [
- {
- title: 'W3C Validation',
- namedParams: { parser: 'html' },
- queryParams: {
- targetUrl: 'https://validator.nu/',
- preset: 'HTML, SVG 1.1, MathML 3.0',
- },
- staticPreview: this.render({ messageTypes: {} }),
- documentation,
+ static openApi = {
+ '/w3c-validation/{parser}': {
+ get: {
+ summary: 'W3C Validation',
+ description,
+ parameters: [
+ pathParam({
+ name: 'parser',
+ example: 'html',
+ schema: { type: 'string', enum: this.getEnum('parser') },
+ description: parserDescription,
+ }),
+ queryParam({
+ name: 'targetUrl',
+ example: 'https://validator.nu/',
+ required: true,
+ description: 'URL of the document to be validate',
+ }),
+ queryParam({
+ name: 'preset',
+ example: 'HTML, SVG 1.1, MathML 3.0',
+ description:
+ 'This is used to determine the schema for the document to be valdiated against.',
+ schema: {
+ type: 'string',
+ enum: [
+ 'HTML, SVG 1.1, MathML 3.0',
+ 'HTML, SVG 1.1, MathML 3.0, ITS 2.0',
+ 'HTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1',
+ 'HTML 4.01 Strict, URL / XHTML 1.0 Strict, URL',
+ 'HTML 4.01 Transitional, URL / XHTML 1.0 Transitional, URL',
+ 'HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL',
+ 'XHTML, SVG 1.1, MathML 3.0',
+ 'XHTML, SVG 1.1, MathML 3.0, RDFa Lite 1.1',
+ 'XHTML 1.0 Strict, URL, Ruby, SVG 1.1, MathML 3.0',
+ 'SVG 1.1, URL, XHTML, MathML 3.0',
+ ],
+ },
+ }),
+ ],
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'w3c',
- }
+ static defaultBadgeData = {
+ label: 'w3c',
}
static render({ messageTypes }) {
@@ -78,7 +105,7 @@ module.exports = class W3cValidation extends BaseJsonService {
url: 'https://validator.nu/',
schema,
options: {
- qs: {
+ searchParams: {
schema: getSchema(preset),
parser: parser === 'default' ? undefined : parser,
doc: encodeURI(targetUrl),
diff --git a/services/w3c/w3c-validation.tester.js b/services/w3c/w3c-validation.tester.js
index b2eff4457c3b4..f7cc4880c62c8 100644
--- a/services/w3c/w3c-validation.tester.js
+++ b/services/w3c/w3c-validation.tester.js
@@ -1,27 +1,27 @@
-'use strict'
-const Joi = require('@hapi/joi')
-const t = (module.exports = require('../tester').createServiceTester())
+import Joi from 'joi'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
const isErrorOnly = Joi.string().regex(/^[0-9]+ errors?$/)
const isWarningOnly = Joi.string().regex(/^[0-9]+ warnings?$/)
const isErrorAndWarning = Joi.string().regex(
- /^[0-9]+ errors?, [0-9]+ warnings?$/
+ /^[0-9]+ errors?, [0-9]+ warnings?$/,
)
const isW3CMessage = Joi.alternatives().try(
'validated',
isErrorOnly,
isWarningOnly,
- isErrorAndWarning
+ isErrorAndWarning,
)
const isW3CColors = Joi.alternatives().try('brightgreen', 'red', 'yellow')
t.create(
- 'W3C Validation page conforms to standards with no preset and parser with brightgreen badge'
+ 'W3C Validation page conforms to standards with no preset and parser with brightgreen badge',
)
.get(
- '/default.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html'
+ '/default.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html',
)
.expectBadge({
label: 'w3c',
@@ -30,10 +30,10 @@ t.create(
})
t.create(
- 'W3C Validation page conforms to standards with no HTML4 preset and HTML parser with brightgreen badge'
+ 'W3C Validation page conforms to standards with no HTML4 preset and HTML parser with brightgreen badge',
)
.get(
- '/html.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html&preset=HTML,%20SVG%201.1,%20MathML%203.0'
+ '/html.json?targetUrl=https://hsivonen.com/test/moz/messages-types/no-message.html&preset=HTML,%20SVG%201.1,%20MathML%203.0',
)
.expectBadge({
label: 'w3c',
@@ -43,7 +43,7 @@ t.create(
t.create('W3C Validation target url not found error')
.get(
- '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/404.html'
+ '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/404.html',
)
.expectBadge({
label: 'w3c',
@@ -59,7 +59,7 @@ t.create('W3C Validation target url host not found error')
t.create('W3C Validation page has 1 validation error with red badge')
.get(
- '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html'
+ '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html',
)
.expectBadge({
label: 'w3c',
@@ -68,10 +68,10 @@ t.create('W3C Validation page has 1 validation error with red badge')
})
t.create(
- 'W3C Validation page has 3 validation error using HTML 4.01 Frameset preset with red badge'
+ 'W3C Validation page has 3 validation error using HTML 4.01 Frameset preset with red badge',
)
.get(
- '/html.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html&preset=HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL'
+ '/html.json?targetUrl=http://hsivonen.com/test/moz/messages-types/warning.html&preset=HTML 4.01 Frameset, URL / XHTML 1.0 Frameset, URL',
)
.expectBadge({
label: 'w3c',
@@ -81,7 +81,7 @@ t.create(
t.create('W3C Validation page has 1 validation warning with yellow badge')
.get(
- '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/info.svg'
+ '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/info.svg',
)
.expectBadge({
label: 'w3c',
@@ -91,7 +91,7 @@ t.create('W3C Validation page has 1 validation warning with yellow badge')
t.create('W3C Validation page has multiple of validation errors with red badge')
.get(
- '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/range-error.html'
+ '/default.json?targetUrl=http://hsivonen.com/test/moz/messages-types/range-error.html',
)
.expectBadge({
label: 'w3c',
diff --git a/services/waffle/waffle-label-redirect.service.js b/services/waffle/waffle-label-redirect.service.js
deleted file mode 100644
index 0275bc1d5cbf3..0000000000000
--- a/services/waffle/waffle-label-redirect.service.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict'
-
-const { redirector } = require('..')
-
-module.exports = [
- // The WaffleLabel service originally had 'label' as an optional parameter
- // with a default value of 'ready'. However, after some discussion it was
- // agreed that 'ready' didn't make sense as a default, nor was there any other
- // value that would be a reasonable default. As such, 'label' is now a required
- // param and this redirect service maintains backwards compatibility.
- // See https://github.com/badges/shields/pull/3133#discussion_r261882687
- redirector({
- category: 'issue-tracking',
- route: {
- base: 'waffle/label',
- pattern: ':user/:repo',
- },
- transformPath: ({ user, repo }) => `/waffle/label/${user}/${repo}/ready`,
- dateAdded: new Date('2019-03-05'),
- }),
-]
diff --git a/services/waffle/waffle-label-redirect.tester.js b/services/waffle/waffle-label-redirect.tester.js
deleted file mode 100644
index fb1b0cd6cabfa..0000000000000
--- a/services/waffle/waffle-label-redirect.tester.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'WaffleLabelRedirect',
- title: 'WaffleLabelRedirect',
- pathPrefix: '/waffle/label',
-}))
-
-t.create('waffle label redirect')
- .get('/waffleio/waffle.io.svg')
- .expectRedirect('/waffle/label/waffleio/waffle.io/ready.svg')
diff --git a/services/waffle/waffle-label.service.js b/services/waffle/waffle-label.service.js
deleted file mode 100644
index de28fb92643e2..0000000000000
--- a/services/waffle/waffle-label.service.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict'
-
-const { deprecatedService } = require('..')
-
-module.exports = deprecatedService({
- route: {
- base: 'waffle/label',
- pattern: ':various*',
- },
- label: 'waffle',
- category: 'issue-tracking',
- dateAdded: new Date('2019-05-21'),
-})
diff --git a/services/waffle/waffle-label.tester.js b/services/waffle/waffle-label.tester.js
deleted file mode 100644
index 98158d93fc8ff..0000000000000
--- a/services/waffle/waffle-label.tester.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict'
-
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
- id: 'Waffle',
- title: 'WaffleLabel',
- pathPrefix: '/waffle/label',
-}))
-
-t.create('no longer available')
- .get('/ritwickdey/vscode-live-server/bug.json')
- .expectBadge({
- label: 'waffle',
- message: 'no longer available',
- })
diff --git a/services/weblate/weblate-base.js b/services/weblate/weblate-base.js
new file mode 100644
index 0000000000000..da9d0cc5d7474
--- /dev/null
+++ b/services/weblate/weblate-base.js
@@ -0,0 +1,28 @@
+import Joi from 'joi'
+import { BaseJsonService } from '../index.js'
+import { optionalUrl } from '../validators.js'
+
+export const defaultServer = 'https://hosted.weblate.org'
+export const description =
+ 'Weblate is an web-based tool for translation and internationalization'
+
+export default class WeblateBase extends BaseJsonService {
+ static queryParamSchema = Joi.object({
+ server: optionalUrl,
+ }).required()
+
+ static auth = {
+ passKey: 'weblate_api_key',
+ isRequired: false,
+ serviceKey: 'weblate',
+ }
+
+ async fetch(requestParams) {
+ return this._requestJson(
+ this.authHelper.withBearerAuthHeader(
+ requestParams,
+ 'Token', // lgtm [js/hardcoded-credentials]
+ ),
+ )
+ }
+}
diff --git a/services/weblate/weblate-component-license.service.js b/services/weblate/weblate-component-license.service.js
new file mode 100644
index 0000000000000..0771dd96eb1ff
--- /dev/null
+++ b/services/weblate/weblate-component-license.service.js
@@ -0,0 +1,57 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import WeblateBase, { defaultServer, description } from './weblate-base.js'
+
+const schema = Joi.object({
+ license: Joi.string().required(),
+}).required()
+
+/**
+ * This badge displays the license of a component on a Weblate instance.
+ */
+export default class WeblateComponentLicense extends WeblateBase {
+ static category = 'license'
+
+ static route = {
+ base: 'weblate/l',
+ pattern: ':project/:component',
+ queryParamSchema: this.queryParamSchema,
+ }
+
+ static openApi = {
+ '/weblate/l/{project}/{component}': {
+ get: {
+ summary: 'Weblate component license',
+ description,
+ parameters: [
+ pathParam({ name: 'project', example: 'godot-engine' }),
+ pathParam({ name: 'component', example: 'godot' }),
+ queryParam({ name: 'server', example: defaultServer }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'license', color: 'informational' }
+
+ static render({ license }) {
+ return { message: `${license}` }
+ }
+
+ async fetch({ project, component, server = defaultServer }) {
+ return super.fetch({
+ schema,
+ url: `${server}/api/components/${project}/${component}/`,
+ httpErrors: {
+ 403: 'access denied by remote server',
+ 404: 'component not found',
+ },
+ logErrors: server === defaultServer ? [429] : [],
+ })
+ }
+
+ async handle({ project, component }, { server }) {
+ const { license } = await this.fetch({ project, component, server })
+ return this.constructor.render({ license })
+ }
+}
diff --git a/services/weblate/weblate-component-license.tester.js b/services/weblate/weblate-component-license.tester.js
new file mode 100644
index 0000000000000..031a2ecc897f8
--- /dev/null
+++ b/services/weblate/weblate-component-license.tester.js
@@ -0,0 +1,10 @@
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('License')
+ .get('/godot-engine/godot.json')
+ .expectBadge({ label: 'license', message: 'MIT' })
+
+t.create("Component Doesn't Exist")
+ .get('/fake-project/fake-component.json?server=https://hosted.weblate.org')
+ .expectBadge({ label: 'license', message: 'component not found' })
diff --git a/services/weblate/weblate-entities.service.js b/services/weblate/weblate-entities.service.js
new file mode 100644
index 0000000000000..738f7a41906b4
--- /dev/null
+++ b/services/weblate/weblate-entities.service.js
@@ -0,0 +1,60 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import WeblateBase, { defaultServer, description } from './weblate-base.js'
+
+const schema = Joi.object({
+ count: nonNegativeInteger,
+}).required()
+
+export default class WeblateEntities extends WeblateBase {
+ static category = 'other'
+
+ static route = {
+ base: 'weblate',
+ pattern: ':type(components|projects|users|languages)',
+ queryParamSchema: this.queryParamSchema,
+ }
+
+ static openApi = {
+ '/weblate/{type}': {
+ get: {
+ summary: 'Weblate entities',
+ description,
+ parameters: [
+ pathParam({
+ name: 'type',
+ example: 'projects',
+ schema: { type: 'string', enum: this.getEnum('type') },
+ }),
+ queryParam({ name: 'server', example: defaultServer }),
+ ],
+ },
+ },
+ }
+
+ static _cacheLength = 600
+
+ static defaultBadgeData = { color: 'informational' }
+
+ static render({ type, count }) {
+ return { label: type, message: metric(count) }
+ }
+
+ async fetch({ type, server = defaultServer }) {
+ return super.fetch({
+ schema,
+ url: `${server}/api/${type}/`,
+ httpErrors: {
+ 403: 'access denied by remote server',
+ },
+ logErrors: server === defaultServer ? [429] : [],
+ })
+ }
+
+ async handle({ type }, { server }) {
+ const { count } = await this.fetch({ type, server })
+ return this.constructor.render({ type, count })
+ }
+}
diff --git a/services/weblate/weblate-entities.tester.js b/services/weblate/weblate-entities.tester.js
new file mode 100644
index 0000000000000..f05c6ab0521fb
--- /dev/null
+++ b/services/weblate/weblate-entities.tester.js
@@ -0,0 +1,19 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Components')
+ .get('/components.json')
+ .expectBadge({ label: 'components', message: isMetric })
+
+t.create('Projects')
+ .get('/projects.json')
+ .expectBadge({ label: 'projects', message: isMetric })
+
+t.create('Users')
+ .get('/users.json?server=https://hosted.weblate.org')
+ .expectBadge({ label: 'users', message: isMetric })
+
+t.create('Components')
+ .get('/components.json?server=https://translate.mattermost.com')
+ .expectBadge({ label: 'components', message: isMetric })
diff --git a/services/weblate/weblate-project-translated-percentage.service.js b/services/weblate/weblate-project-translated-percentage.service.js
new file mode 100644
index 0000000000000..dbeb0a883d6df
--- /dev/null
+++ b/services/weblate/weblate-project-translated-percentage.service.js
@@ -0,0 +1,73 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { colorScale } from '../color-formatters.js'
+import WeblateBase, { defaultServer, description } from './weblate-base.js'
+
+const schema = Joi.object({
+ translated_percent: Joi.number().required(),
+}).required()
+
+/**
+ * This badge displays the percentage of strings translated on a project on a
+ * Weblate instance.
+ */
+export default class WeblateProjectTranslatedPercentage extends WeblateBase {
+ static category = 'other'
+
+ static route = {
+ base: 'weblate/progress',
+ pattern: ':project',
+ queryParamSchema: this.queryParamSchema,
+ }
+
+ static openApi = {
+ '/weblate/progress/{project}': {
+ get: {
+ summary: 'Weblate project translated',
+ description,
+ parameters: [
+ pathParam({ name: 'project', example: 'godot-engine' }),
+ queryParam({ name: 'server', example: defaultServer }),
+ ],
+ },
+ },
+ }
+
+ static _cacheLength = 600
+
+ static defaultBadgeData = { label: 'translated' }
+
+ /**
+ * Takes a percentage and maps it to a message and color.
+ *
+ * The colors are determined based on how Weblate does it internally.
+ * {@link https://github.com/WeblateOrg/weblate/blob/main/weblate/trans/widgets.py Weblate on GitHub}
+ *
+ * @param {*} translatedPercent The percentage of translations translated.
+ * @returns {object} Format for the badge.
+ */
+ static render({ translatedPercent }) {
+ const color = colorScale([75, 90])(translatedPercent)
+ return { message: `${translatedPercent.toFixed(0)}%`, color }
+ }
+
+ async fetch({ project, server = defaultServer }) {
+ return super.fetch({
+ schema,
+ url: `${server}/api/projects/${project}/statistics/`,
+ httpErrors: {
+ 403: 'access denied by remote server',
+ 404: 'project not found',
+ },
+ logErrors: server === defaultServer ? [429] : [],
+ })
+ }
+
+ async handle({ project }, { server }) {
+ const { translated_percent: translatedPercent } = await this.fetch({
+ project,
+ server,
+ })
+ return this.constructor.render({ translatedPercent })
+ }
+}
diff --git a/services/weblate/weblate-project-translated-percentage.tester.js b/services/weblate/weblate-project-translated-percentage.tester.js
new file mode 100644
index 0000000000000..ef20cc4486359
--- /dev/null
+++ b/services/weblate/weblate-project-translated-percentage.tester.js
@@ -0,0 +1,11 @@
+import { createServiceTester } from '../tester.js'
+import { isPercentage } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('License')
+ .get('/godot-engine.json')
+ .expectBadge({ label: 'translated', message: isPercentage })
+
+t.create('Not Valid')
+ .get('/fake-project.json?server=https://hosted.weblate.org')
+ .expectBadge({ label: 'translated', message: 'project not found' })
diff --git a/services/weblate/weblate-user-statistic.service.js b/services/weblate/weblate-user-statistic.service.js
new file mode 100644
index 0000000000000..c490c048e1401
--- /dev/null
+++ b/services/weblate/weblate-user-statistic.service.js
@@ -0,0 +1,76 @@
+import Joi from 'joi'
+import { pathParam, queryParam } from '../index.js'
+import { nonNegativeInteger } from '../validators.js'
+import { metric } from '../text-formatters.js'
+import WeblateBase, { defaultServer, description } from './weblate-base.js'
+
+const schema = Joi.object({
+ translated: nonNegativeInteger,
+ suggested: nonNegativeInteger,
+ uploaded: nonNegativeInteger,
+ commented: nonNegativeInteger,
+ languages: nonNegativeInteger,
+}).required()
+
+const statisticKeyNames = {
+ translations: 'translated',
+ suggestions: 'suggested',
+ uploads: 'uploaded',
+ comments: 'commented',
+ languages: 'languages',
+}
+
+export default class WeblateUserStatistic extends WeblateBase {
+ static category = 'other'
+
+ static route = {
+ base: 'weblate',
+ pattern:
+ ':statistic(translations|suggestions|languages|uploads|comments)/:user',
+ queryParamSchema: this.queryParamSchema,
+ }
+
+ static openApi = {
+ '/weblate/{statistic}/{user}': {
+ get: {
+ summary: 'Weblate user statistic',
+ description,
+ parameters: [
+ pathParam({
+ name: 'statistic',
+ example: 'translations',
+ schema: { type: 'string', enum: this.getEnum('statistic') },
+ }),
+ pathParam({ name: 'user', example: 'nijel' }),
+ queryParam({ name: 'server', example: defaultServer }),
+ ],
+ },
+ },
+ }
+
+ static _cacheLength = 600
+
+ static defaultBadgeData = { color: 'informational' }
+
+ static render({ statistic, count }) {
+ return { label: statistic, message: metric(count) }
+ }
+
+ async fetch({ user, server = defaultServer }) {
+ return super.fetch({
+ schema,
+ url: `${server}/api/users/${user}/statistics/`,
+ httpErrors: {
+ 403: 'access denied by remote server',
+ 404: 'user not found',
+ },
+ logErrors: server === defaultServer ? [429] : [],
+ })
+ }
+
+ async handle({ user, statistic }, { server }) {
+ const data = await this.fetch({ user, server })
+ const key = statisticKeyNames[statistic]
+ return this.constructor.render({ statistic, count: data[key] })
+ }
+}
diff --git a/services/weblate/weblate-user-statistic.tester.js b/services/weblate/weblate-user-statistic.tester.js
new file mode 100644
index 0000000000000..d2fc7e00e68ad
--- /dev/null
+++ b/services/weblate/weblate-user-statistic.tester.js
@@ -0,0 +1,15 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('Translations')
+ .get('/translations/nijel.json')
+ .expectBadge({ label: 'translations', message: isMetric })
+
+t.create('Suggestions')
+ .get('/suggestions/nijel.json')
+ .expectBadge({ label: 'suggestions', message: isMetric })
+
+t.create('Languages')
+ .get('/languages/nijel.json?server=https://hosted.weblate.org')
+ .expectBadge({ label: 'languages', message: isMetric })
diff --git a/services/website-status.js b/services/website-status.js
index 1f36662ca4a5a..832f56ad78b6a 100644
--- a/services/website-status.js
+++ b/services/website-status.js
@@ -1,7 +1,20 @@
-'use strict'
+/**
+ * Commonly used functions and utilities for tasks related to website status.
+ *
+ * @module
+ */
-const Joi = require('@hapi/joi')
+import Joi from 'joi'
+import { queryParams as qP } from './index.js'
+/** @import { OpenApiParam } from '../core/base-service/openapi.js' */
+
+/**
+ * Joi schema for validating query params.
+ * Checks if the query params object has valid up_message, down_message, up_color and down_color properties.
+ *
+ * @type {Joi}
+ */
const queryParamSchema = Joi.object({
up_message: Joi.string(),
down_message: Joi.string(),
@@ -9,13 +22,35 @@ const queryParamSchema = Joi.object({
down_color: Joi.alternatives(Joi.string(), Joi.number()),
}).required()
-const exampleQueryParams = {
- up_message: 'online',
- up_color: 'blue',
- down_message: 'offline',
- down_color: 'lightgrey',
-}
+/**
+ * Array of OpenAPI Parameter Objects describing the
+ * up_message, down_message, up_color and down_color
+ * query params
+ *
+ * @type {Array.}
+ * @see {@link module:core/base-service/openapi~OpenApiParam}
+ */
+const queryParams = qP(
+ { name: 'up_message', example: 'online' },
+ { name: 'up_color', example: 'blue' },
+ { name: 'down_message', example: 'offline' },
+ { name: 'down_color', example: 'lightgrey' },
+)
+/**
+ * Creates a badge object that displays information about website status.
+ *
+ * @param {object} options - The options for rendering the status
+ * @param {boolean} options.isUp - Whether the website or service is up or down
+ * @param {string} [options.upMessage='up'] - The message to display when the website or service is up
+ * @param {string} [options.downMessage='down'] - The message to display when the website or service is down
+ * @param {string} [options.upColor='brightgreen'] - The color to use when the website or service is up
+ * @param {string} [options.downColor='red'] - The color to use when the website or service is down
+ * @returns {object} An object with a message and a color property
+ * @example
+ * renderWebsiteStatus({ isUp: true }) // returns { message: 'up', color: 'brightgreen' }
+ * renderWebsiteStatus({ isUp: false }) // returns { message: 'down', color: 'red' }
+ */
function renderWebsiteStatus({
isUp,
upMessage = 'up',
@@ -30,8 +65,4 @@ function renderWebsiteStatus({
}
}
-module.exports = {
- queryParamSchema,
- exampleQueryParams,
- renderWebsiteStatus,
-}
+export { queryParamSchema, queryParams, renderWebsiteStatus }
diff --git a/services/website-status.spec.js b/services/website-status.spec.js
index b27771fcecacd..5e73cd774292f 100644
--- a/services/website-status.spec.js
+++ b/services/website-status.spec.js
@@ -1,9 +1,7 @@
-'use strict'
+import { test, given } from 'sazerac'
+import { renderWebsiteStatus } from './website-status.js'
-const { test, given } = require('sazerac')
-const { renderWebsiteStatus } = require('./website-status')
-
-describe('Website status helpers', function() {
+describe('Website status helpers', function () {
const customOptions = {
upMessage: 'groovy',
upColor: 'papayawhip',
diff --git a/services/website/website-redirect.service.js b/services/website/website-redirect.service.js
index f2597dd1ceb72..55dd75f19dd1f 100644
--- a/services/website/website-redirect.service.js
+++ b/services/website/website-redirect.service.js
@@ -1,7 +1,5 @@
-'use strict'
-
-const { escapeFormat } = require('../../core/badge-urls/path-helpers')
-const { redirector } = require('..')
+import { escapeFormat } from '../../core/badge-urls/path-helpers.js'
+import { redirector } from '../index.js'
function escapeFormatSlashes(t) {
return (
@@ -11,6 +9,31 @@ function escapeFormatSlashes(t) {
)
}
+function splitDashSeparatedOptionalParams(s) {
+ const parts = []
+ let cur = ''
+ for (let i = 0; i < s.length;) {
+ const ch = s[i]
+ const next = s[i + 1]
+ if (ch === '-' && next === '-') {
+ cur += '-'
+ i += 2
+ } else if (ch === '/' && next === '/') {
+ cur += '/'
+ i += 2
+ } else if (ch === '-') {
+ parts.push(cur)
+ cur = ''
+ i += 1
+ } else {
+ cur += ch
+ i += 1
+ }
+ }
+ parts.push(cur)
+ return parts
+}
+
/*
Old documentation, for reference:
@@ -51,44 +74,28 @@ Old documentation, for reference:
*/
-module.exports = [
+export default [
redirector({
category: 'monitoring',
route: {
base: '',
- format:
- 'website-(([^-/]|--|//)+)-(([^-/]|--|//)+)(-(([^-/]|--|//)+)-(([^-/]|--|//)+))?/([^/]+)/(.+?)',
- capture: [
- // Some of these could be made into non-capturing groups so these unused
- // params would not need to be declared.
- 'upMessage',
- 'unused2',
- 'downMessage',
- 'unused4',
- 'unused5',
- 'upColor',
- 'unused7',
- 'downColor',
- 'unused8',
- 'protocol',
- 'hostAndPath',
- ],
+ pattern: 'website-:labels/:protocol/:hostAndPath+',
},
transformPath: () => '/website',
- transformQueryParams: ({
- upMessage,
- downMessage,
- upColor,
- downColor,
- protocol,
- hostAndPath,
- }) => ({
- up_message: upMessage ? escapeFormatSlashes(upMessage) : undefined,
- down_message: downMessage ? escapeFormatSlashes(downMessage) : undefined,
- up_color: upColor,
- down_color: downColor,
- url: `${protocol}://${hostAndPath}`,
- }),
+ transformQueryParams: ({ labels, protocol, hostAndPath }) => {
+ const parts = splitDashSeparatedOptionalParams(labels || '')
+ const [upMessage, downMessage, upColor, downColor] = parts
+
+ return {
+ up_message: upMessage ? escapeFormatSlashes(upMessage) : undefined,
+ down_message: downMessage
+ ? escapeFormatSlashes(downMessage)
+ : undefined,
+ up_color: upColor,
+ down_color: downColor,
+ url: `${protocol}://${hostAndPath}`,
+ }
+ },
dateAdded: new Date('2019-03-08'),
}),
redirector({
diff --git a/services/website/website-redirect.tester.js b/services/website/website-redirect.tester.js
index 4e0b27bca2d73..f592b4b88f360 100644
--- a/services/website/website-redirect.tester.js
+++ b/services/website/website-redirect.tester.js
@@ -1,35 +1,33 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'WebsiteRedirect',
title: 'WebsiteRedirect',
pathPrefix: '',
-}))
+})
t.create('Website with custom messages')
.get('/website-up-down/https/www.google.com.svg')
.expectRedirect(
- `/website.svg?down_message=down&up_message=up&url=${encodeURIComponent(
- 'https://www.google.com'
- )}`
+ `/website.svg?up_message=up&down_message=down&url=${encodeURIComponent(
+ 'https://www.google.com',
+ )}`,
)
t.create('Website with custom messages and colors')
.get('/website-up-down-yellow-gray/https/www.google.com.svg')
.expectRedirect(
- `/website.svg?down_color=gray&down_message=down&up_color=yellow&up_message=up&url=${encodeURIComponent(
- 'https://www.google.com'
- )}`
+ `/website.svg?up_message=up&down_message=down&up_color=yellow&down_color=gray&url=${encodeURIComponent(
+ 'https://www.google.com',
+ )}`,
)
t.create('Website to queryParam with custom messages and colors')
.get(
- '/website/https/www.google.com.svg?down_color=gray&down_message=down&up_color=yellow&up_message=up'
+ '/website/https/www.google.com.svg?down_color=gray&down_message=down&up_color=yellow&up_message=up',
)
.expectRedirect(
`/website.svg?down_color=gray&down_message=down&up_color=yellow&up_message=up&url=${encodeURIComponent(
- 'https://www.google.com'
- )}`
+ 'https://www.google.com',
+ )}`,
)
diff --git a/services/website/website.service.js b/services/website/website.service.js
index cac32bff0a822..fbfefc5e2fe25 100644
--- a/services/website/website.service.js
+++ b/services/website/website.service.js
@@ -1,70 +1,62 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { optionalUrl } = require('../validators')
-const {
+import emojic from 'emojic'
+import Joi from 'joi'
+import trace from '../../core/base-service/trace.js'
+import { BaseService, queryParams } from '../index.js'
+import { url } from '../validators.js'
+import {
queryParamSchema,
- exampleQueryParams,
renderWebsiteStatus,
-} = require('../website-status')
-const { BaseService } = require('..')
+ queryParams as websiteQueryParams,
+} from '../website-status.js'
+
+const description = `
+The existence of a specific path on the server can be checked by appending
+a path after the domain name, e.g.
+\`https://img.shields.io/website?url=http%3A//www.website.com/path/to/page.html\`.
+
+The messages and colors for the up and down states can also be customized.
-const documentation = `
-
- The badge is of the form
- https://img.shields.io/website/PROTOCOL/URLREST.svg.
-
-
- The whole URL is obtained by concatenating the PROTOCOL
- (http or https, for example) with the
- URLREST (separating them with ://).
-
-
- The existence of a specific path on the server can be checked by appending
- a path after the domain name, e.g.
- https://img.shields.io/website/http/www.website.com/path/to/page.html.svg.
-
-
- The messages and colors for the up and down states can also be customized.
-
+A site will be classified as "down" if it fails to respond within 3.5 seconds.
`
const urlQueryParamSchema = Joi.object({
- url: optionalUrl.required(),
+ url,
}).required()
-module.exports = class Website extends BaseService {
- static get category() {
- return 'monitoring'
- }
+export default class Website extends BaseService {
+ static category = 'monitoring'
- static get route() {
- return {
- base: '',
- pattern: 'website',
- queryParamSchema: queryParamSchema.concat(urlQueryParamSchema),
- }
+ static route = {
+ base: '',
+ pattern: 'website',
+ queryParamSchema: queryParamSchema.concat(urlQueryParamSchema),
}
- static get examples() {
- return [
- {
- title: 'Website',
- namedParams: {},
- queryParams: {
- ...exampleQueryParams,
- ...{ url: 'https://shields.io' },
- },
- staticPreview: renderWebsiteStatus({ isUp: true }),
- documentation,
+ static openApi = {
+ '/website': {
+ get: {
+ summary: 'Website',
+ description,
+ parameters: queryParams({
+ name: 'url',
+ required: true,
+ example: 'https://shields.io',
+ }).concat(websiteQueryParams),
},
- ]
+ },
}
- static get defaultBadgeData() {
- return {
- label: 'website',
- }
+ static defaultBadgeData = {
+ label: 'website',
+ }
+
+ async _request({ url, options = {} }) {
+ const logTrace = (...args) => trace.logTrace('fetch', ...args)
+ logTrace(emojic.bowAndArrow, 'Request', url, '\n', options)
+ const { res, buffer } = await this._requestFetcher(url, options)
+ await this._meterResponse(res, buffer)
+ logTrace(emojic.dart, 'Response status code', res.statusCode)
+ return { res, buffer }
}
async handle(
@@ -75,7 +67,7 @@ module.exports = class Website extends BaseService {
up_color: upColor,
down_color: downColor,
url,
- }
+ },
) {
let isUp
try {
@@ -85,6 +77,9 @@ module.exports = class Website extends BaseService {
url,
options: {
method: 'HEAD',
+ timeout: {
+ response: 3500,
+ },
},
})
// We consider all HTTP status codes below 310 as success.
diff --git a/services/website/website.tester.js b/services/website/website.tester.js
index 3e9ae21bd05b7..0aec420ab0c90 100644
--- a/services/website/website.tester.js
+++ b/services/website/website.tester.js
@@ -1,42 +1,68 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('status of http://shields.io')
.get('/website.json?url=http://shields.io')
+ .timeout(7500)
.expectBadge({ label: 'website', message: 'up', color: 'brightgreen' })
t.create('status of https://shields.io')
.get('/website.json?url=https://shields.io')
+ .timeout(7500)
.expectBadge({ label: 'website', message: 'up', color: 'brightgreen' })
t.create('status of nonexistent domain')
- .get('/website.json?url=http://shields.io.io')
+ .get('/website.json?url=https://shields.io.io')
+ .timeout(15000)
.expectBadge({ label: 'website', message: 'down', color: 'red' })
t.create('status when network is off')
- .get('/website.json?url=http://shields.io')
+ .get('/website.json?url=https://shields.io')
.networkOff()
.expectBadge({ label: 'website', message: 'down', color: 'red' })
+t.create('status is up if response code is 201')
+ .get('/website.json?url=http://online.com')
+ .intercept(nock => nock('http://online.com').head('/').reply(201))
+ .expectBadge({ label: 'website', message: 'up' })
+
+t.create('status is up if response code is 299')
+ .get('/website.json?url=http://online.com')
+ .intercept(nock => nock('http://online.com').head('/').reply(299))
+ .expectBadge({ label: 'website', message: 'up' })
+
+t.create('status is up if response code is 301')
+ .get('/website.json?url=http://online.com')
+ .intercept(nock => nock('http://online.com').head('/').reply(301))
+ .expectBadge({ label: 'website', message: 'up' })
+
+t.create('status is up if response code is 309')
+ .get('/website.json?url=http://online.com')
+ .intercept(nock => nock('http://online.com').head('/').reply(309))
+ .expectBadge({ label: 'website', message: 'up' })
+
+t.create('status is down if response code is 401')
+ .get('/website.json?url=http://offline.com')
+ .intercept(nock => nock('http://offline.com').head('/').reply(401))
+ .expectBadge({ label: 'website', message: 'down' })
+
+t.create('status is down if it is unresponsive for more than 3500 ms')
+ .get('/website.json?url=http://offline.com')
+ .intercept(nock =>
+ nock('http://offline.com').head('/').delay(4000).reply(200),
+ )
+ .expectBadge({ label: 'website', message: 'down' })
+
t.create('custom online label, online message and online color')
.get(
- '/website.json?url=http://online.com&up_message=up&down_message=down&up_color=green&down_color=grey'
- )
- .intercept(nock =>
- nock('http://online.com')
- .head('/')
- .reply(200)
+ '/website.json?url=http://online.com&up_message=up&down_message=down&up_color=green&down_color=grey',
)
+ .intercept(nock => nock('http://online.com').head('/').reply(200))
.expectBadge({ label: 'website', message: 'up', color: 'green' })
t.create('custom offline message and offline color')
.get(
- '/website.json?url=http://offline.com&up_message=up&down_message=down&up_color=green&down_color=grey'
- )
- .intercept(nock =>
- nock('http://offline.com')
- .head('/')
- .reply(500)
+ '/website.json?url=http://offline.com&up_message=up&down_message=down&up_color=green&down_color=grey',
)
+ .intercept(nock => nock('http://offline.com').head('/').reply(500))
.expectBadge({ label: 'website', message: 'down', color: 'grey' })
diff --git a/services/wercker/wercker.service.js b/services/wercker/wercker.service.js
deleted file mode 100644
index cd25d2a94c086..0000000000000
--- a/services/wercker/wercker.service.js
+++ /dev/null
@@ -1,135 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
-const { BaseJsonService } = require('..')
-
-const werckerSchema = Joi.array()
- .items(
- Joi.object({
- result: isBuildStatus,
- })
- )
- .min(0)
- .max(1)
- .required()
-
-const werckerCIDocumentation = `
-
- Note that Wercker badge Key (used in Wercker's native badge urls) is not the same as
- the Application Id and the badge key will not work.
-
-
- You can use the Wercker API to locate your Application Id:
-
-
- https://app.wercker.com/api/v3/applications/:username/:applicationName
-
- For example: https://app.wercker.com/api/v3/applications/wercker/go-wercker-api
-
-
- Your Application Id will be in the 'id' field in the API response.
-
-`
-
-module.exports = class Wercker extends BaseJsonService {
- static get category() {
- return 'build'
- }
-
- static get route() {
- return {
- base: 'wercker',
- format:
- '(?:(?:ci/)([a-fA-F0-9]{24})|(?:build|ci)/([^/]+/[^/]+?))(?:/(.+?))?',
- capture: ['projectId', 'applicationName', 'branch'],
- }
- }
-
- static get examples() {
- return [
- {
- title: `Wercker CI Run`,
- pattern: 'ci/:applicationId',
- namedParams: { applicationId: '559e33c8e982fc615500b357' },
- staticPreview: this.render({ result: 'passed' }),
- documentation: werckerCIDocumentation,
- },
- {
- title: `Wercker CI Run`,
- pattern: 'ci/:applicationId/:branch',
- namedParams: {
- applicationId: '559e33c8e982fc615500b357',
- branch: 'master',
- },
- staticPreview: this.render({ result: 'passed' }),
- documentation: werckerCIDocumentation,
- },
- {
- title: `Wercker Build`,
- pattern: 'build/:userName/:applicationName',
- namedParams: {
- userName: 'wercker',
- applicationName: 'go-wercker-api',
- },
- staticPreview: this.render({ result: 'passed' }),
- },
- {
- title: `Wercker Build branch`,
- pattern: 'build/:userName/:applicationName/:branch',
- namedParams: {
- userName: 'wercker',
- applicationName: 'go-wercker-api',
- branch: 'master',
- },
- staticPreview: this.render({ result: 'passed' }),
- },
- ]
- }
-
- static render({ result }) {
- return renderBuildStatusBadge({ status: result })
- }
-
- static getBaseUrl({ projectId, applicationName }) {
- if (applicationName) {
- return `https://app.wercker.com/api/v3/applications/${applicationName}/builds`
- } else {
- return `https://app.wercker.com/api/v3/runs?applicationId=${projectId}`
- }
- }
-
- async fetch({ baseUrl, branch }) {
- return this._requestJson({
- schema: werckerSchema,
- url: baseUrl,
- options: {
- qs: {
- branch,
- limit: 1,
- },
- },
- errorMessages: {
- 401: 'private application not supported',
- 404: 'application not found',
- },
- })
- }
-
- async handle({ projectId, applicationName, branch }) {
- const json = await this.fetch({
- baseUrl: this.constructor.getBaseUrl({
- projectId,
- applicationName,
- }),
- branch,
- })
- if (json.length === 0) {
- return this.constructor.render({
- result: 'not built',
- })
- }
- const { result } = json[0]
- return this.constructor.render({ result })
- }
-}
diff --git a/services/wercker/wercker.tester.js b/services/wercker/wercker.tester.js
deleted file mode 100644
index 28cba83661019..0000000000000
--- a/services/wercker/wercker.tester.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const { isBuildStatus } = require('../build-status')
-const t = (module.exports = require('../tester').createServiceTester())
-
-t.create('Build status')
- .get('/build/wercker/go-wercker-api.json')
- .expectBadge({ label: 'build', message: isBuildStatus })
-
-t.create('Build status (with branch)')
- .get('/build/wercker/go-wercker-api/master.json')
- .expectBadge({ label: 'build', message: isBuildStatus })
-
-t.create('Build status (application not found)')
- .get('/build/some-project/that-doesnt-exist.json')
- .expectBadge({ label: 'build', message: 'application not found' })
-
-t.create('Build status (private application)')
- .get('/build/wercker/blueprint.json')
- .expectBadge({ label: 'build', message: 'private application not supported' })
-
-t.create('Build passed')
- .get('/build/wercker/go-wercker-api.json')
- .intercept(nock =>
- nock('https://app.wercker.com/api/v3/applications/')
- .get('/wercker/go-wercker-api/builds?limit=1')
- .reply(200, [{ status: 'finished', result: 'passed' }])
- )
- .expectBadge({
- label: 'build',
- message: 'passing',
- color: 'brightgreen',
- })
-
-t.create('Build failed')
- .get('/build/wercker/go-wercker-api.json')
- .intercept(nock =>
- nock('https://app.wercker.com/api/v3/applications/')
- .get('/wercker/go-wercker-api/builds?limit=1')
- .reply(200, [{ status: 'finished', result: 'failed' }])
- )
- .expectBadge({ label: 'build', message: 'failing', color: 'red' })
-
-t.create('CI status by ID')
- .get('/ci/559e33c8e982fc615500b357.json')
- .expectBadge({ label: 'build', message: isBuildStatus })
-
-t.create('CI status by ID (with branch)')
- .get('/ci/559e33c8e982fc615500b357/master.json')
- .expectBadge({ label: 'build', message: isBuildStatus })
-
-t.create('CI status by ID (no runs yet)')
- .get('/ci/559e33c8e982fc615500b357.json')
- .intercept(nock =>
- nock('https://app.wercker.com/api/v3')
- .get('/runs?applicationId=559e33c8e982fc615500b357&limit=1')
- .reply(200, [])
- )
- .expectBadge({ label: 'build', message: 'not built' })
diff --git a/services/whatpulse/whatpulse.service.js b/services/whatpulse/whatpulse.service.js
new file mode 100644
index 0000000000000..dabe0bb1eaf5f
--- /dev/null
+++ b/services/whatpulse/whatpulse.service.js
@@ -0,0 +1,134 @@
+import Joi from 'joi'
+import dayjs from 'dayjs'
+import calendar from 'dayjs/plugin/calendar.js'
+import duration from 'dayjs/plugin/duration.js'
+import relativeTime from 'dayjs/plugin/relativeTime.js'
+import { BaseJsonService, pathParam, queryParam } from '../index.js'
+import { metric as formatMetric, ordinalNumber } from '../text-formatters.js'
+dayjs.extend(calendar)
+dayjs.extend(duration)
+dayjs.extend(relativeTime)
+
+const schema = Joi.object({
+ Keys: Joi.alternatives(Joi.string(), Joi.number()).required(),
+ Clicks: Joi.alternatives(Joi.string(), Joi.number()).required(),
+ UptimeSeconds: Joi.alternatives(Joi.string(), Joi.number()).required(),
+ Download: Joi.string().required(),
+ Upload: Joi.string().required(),
+ Ranks: Joi.object({
+ Keys: Joi.string().required(),
+ Clicks: Joi.string().required(),
+ Download: Joi.string().required(),
+ Upload: Joi.string().required(),
+ Uptime: Joi.string().required(),
+ }),
+}).required()
+
+const queryParamSchema = Joi.object({
+ rank: Joi.equal(''),
+}).required()
+
+export default class WhatPulse extends BaseJsonService {
+ static category = 'activity'
+ static route = {
+ base: 'whatpulse',
+ pattern:
+ ':metric(keys|clicks|uptime|download|upload)/:userType(user|team)/:id',
+ queryParamSchema,
+ }
+
+ static openApi = {
+ '/whatpulse/{metric}/{userType}/{id}': {
+ get: {
+ summary: 'WhatPulse',
+ parameters: [
+ pathParam({
+ name: 'metric',
+ example: 'keys',
+ schema: { type: 'string', enum: this.getEnum('metric') },
+ }),
+ pathParam({
+ name: 'userType',
+ example: 'team',
+ schema: { type: 'string', enum: this.getEnum('userType') },
+ }),
+ pathParam({
+ name: 'id',
+ example: '179734',
+ description:
+ 'Either a user ID (e.g: `179734`) or a group ID (e.g: `dutch power cows`)',
+ }),
+ queryParam({
+ name: 'rank',
+ description: 'show rank instead of value',
+ example: null,
+ schema: { type: 'boolean' },
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = { label: 'whatpulse' }
+
+ static render({ metric, metricValue }) {
+ return {
+ label: metric,
+ message: metricValue,
+ color: 'informational',
+ }
+ }
+
+ async fetch({ userType, id }) {
+ return await this._requestJson({
+ schema,
+ url: `https://api.whatpulse.org/${userType}.php?${userType}=${id}&format=json`,
+ })
+ }
+
+ toLowerKeys(obj) {
+ return Object.keys(obj).reduce((accumulator, key) => {
+ accumulator[key.toLowerCase()] = obj[key]
+ return accumulator
+ }, {})
+ }
+
+ transform({ json, metric }, { rank }) {
+ // We want to compare with lowercase keys from the WhatPulse's API.
+ const jsonLowercase = this.toLowerKeys(json)
+ jsonLowercase.ranks = this.toLowerKeys(json.Ranks)
+
+ // Just metric, no rank.
+ if (rank === undefined) {
+ if (metric === 'uptime') {
+ return dayjs.duration(jsonLowercase.uptimeseconds, 'seconds').humanize()
+ }
+
+ let metricValue
+
+ metricValue = jsonLowercase[metric]
+
+ if (metric === 'keys' || metric === 'clicks') {
+ metricValue = formatMetric(metricValue)
+ }
+
+ if (metric === 'upload' || metric === 'download') {
+ metricValue = metricValue.replace(/([A-Za-z]+)/, ' $1')
+ }
+
+ return metricValue
+ }
+
+ // Rank achieved by the user/team with the given metric.
+ const rankFromResp = jsonLowercase.ranks[metric]
+
+ return ordinalNumber(rankFromResp)
+ }
+
+ async handle({ metric, userType, id }, { rank }) {
+ const json = await this.fetch({ userType, id, metric })
+ const metricValue = this.transform({ json, metric }, { rank })
+
+ return this.constructor.render({ metric, metricValue })
+ }
+}
diff --git a/services/whatpulse/whatpulse.tester.js b/services/whatpulse/whatpulse.tester.js
new file mode 100644
index 0000000000000..4c0b3e47f118d
--- /dev/null
+++ b/services/whatpulse/whatpulse.tester.js
@@ -0,0 +1,42 @@
+import { createServiceTester } from '../tester.js'
+import {
+ isMetricFileSize,
+ isHumanized,
+ isMetric,
+ isOrdinalNumber,
+} from '../test-validators.js'
+export const t = await createServiceTester()
+
+t.create('WhatPulse user as user id, uptime')
+ .get('/uptime/user/179734.json')
+ .expectBadge({ label: 'uptime', message: isHumanized })
+
+t.create('WhatPulse user as user name, keys')
+ .get('/keys/user/jerone.json')
+ .expectBadge({ label: 'keys', message: isMetric })
+
+t.create('WhatPulse team as team id, clicks')
+ .get('/clicks/team/1295.json')
+ .expectBadge({ label: 'clicks', message: isMetric })
+
+t.create('WhatPulse team as team id, download')
+ .get('/download/team/1295.json')
+ .expectBadge({ label: 'download', message: isMetricFileSize })
+
+t.create('WhatPulse team as team id, upload')
+ .get('/upload/team/1295.json')
+ .expectBadge({ label: 'upload', message: isMetricFileSize })
+
+t.create('WhatPulse team as team name, keys - from Ranks')
+ .get('/keys/team/dutch power cows.json?rank')
+ .expectBadge({ label: 'keys', message: isOrdinalNumber })
+
+t.create(
+ 'WhatPulse invalid metric name (not one of the options from the modal`s dropdown)',
+)
+ .get('/UpTIMe/user/jerone.json')
+ .expectBadge({ label: '404', message: 'badge not found' })
+
+t.create('WhatPulse incorrect user name')
+ .get('/uptime/user/NonExistentUsername.json')
+ .expectBadge({ label: 'whatpulse', message: 'invalid response data' })
diff --git a/services/wheelmap/wheelmap.service.js b/services/wheelmap/wheelmap.service.js
deleted file mode 100644
index e9158ad5d88de..0000000000000
--- a/services/wheelmap/wheelmap.service.js
+++ /dev/null
@@ -1,79 +0,0 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { BaseJsonService } = require('..')
-
-const schema = Joi.object({
- node: Joi.object({
- wheelchair: Joi.string().required(),
- }).required(),
-}).required()
-
-module.exports = class Wheelmap extends BaseJsonService {
- static get category() {
- return 'other'
- }
-
- static get route() {
- return {
- base: 'wheelmap/a',
- pattern: ':nodeId(-?[0-9]+)',
- }
- }
-
- static get auth() {
- return {
- passKey: 'wheelmap_token',
- authorizedOrigins: ['https://wheelmap.org'],
- isRequired: true,
- }
- }
-
- static get examples() {
- return [
- {
- title: 'Wheelmap',
- namedParams: { nodeId: '26699541' },
- staticPreview: this.render({ accessibility: 'yes' }),
- },
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'accessibility' }
- }
-
- static render({ accessibility }) {
- let color
- if (accessibility === 'yes') {
- color = 'brightgreen'
- } else if (accessibility === 'limited') {
- color = 'yellow'
- } else if (accessibility === 'no') {
- color = 'red'
- }
- return { message: accessibility, color }
- }
-
- async fetch({ nodeId }) {
- return this._requestJson(
- this.authHelper.withQueryStringAuth(
- { passKey: 'api_key' },
- {
- schema,
- url: `https://wheelmap.org/api/nodes/${nodeId}`,
- errorMessages: {
- 401: 'invalid token',
- 404: 'node not found',
- },
- }
- )
- )
- }
-
- async handle({ nodeId }) {
- const json = await this.fetch({ nodeId })
- const accessibility = json.node.wheelchair
- return this.constructor.render({ accessibility })
- }
-}
diff --git a/services/wheelmap/wheelmap.spec.js b/services/wheelmap/wheelmap.spec.js
deleted file mode 100644
index d406aeceb559c..0000000000000
--- a/services/wheelmap/wheelmap.spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict'
-
-const { expect } = require('chai')
-const nock = require('nock')
-const { cleanUpNockAfterEach, defaultContext } = require('../test-helpers')
-const Wheelmap = require('./wheelmap.service')
-
-describe('Wheelmap', function() {
- cleanUpNockAfterEach()
-
- const token = 'abc123'
- const config = { private: { wheelmap_token: token } }
-
- function createMock({ nodeId, wheelchair }) {
- const scope = nock('https://wheelmap.org')
- .get(`/api/nodes/${nodeId}`)
- .query({ api_key: token })
-
- if (wheelchair) {
- return scope.reply(200, { node: { wheelchair } })
- } else {
- return scope.reply(404)
- }
- }
-
- it('node with accessibility', async function() {
- const nodeId = '26699541'
- const scope = createMock({ nodeId, wheelchair: 'yes' })
- expect(
- await Wheelmap.invoke(defaultContext, config, { nodeId })
- ).to.deep.equal({ message: 'yes', color: 'brightgreen' })
- scope.done()
- })
-
- it('node with limited accessibility', async function() {
- const nodeId = '2034868974'
- const scope = createMock({ nodeId, wheelchair: 'limited' })
- expect(
- await Wheelmap.invoke(defaultContext, config, { nodeId })
- ).to.deep.equal({ message: 'limited', color: 'yellow' })
- scope.done()
- })
-
- it('node without accessibility', async function() {
- const nodeId = '-147495158'
- const scope = createMock({ nodeId, wheelchair: 'no' })
- expect(
- await Wheelmap.invoke(defaultContext, config, { nodeId })
- ).to.deep.equal({ message: 'no', color: 'red' })
- scope.done()
- })
-
- it('node not found', async function() {
- const nodeId = '0'
- const scope = createMock({ nodeId })
- expect(
- await Wheelmap.invoke(defaultContext, config, { nodeId })
- ).to.deep.equal({ message: 'node not found', color: 'red', isError: true })
- scope.done()
- })
-})
diff --git a/services/wheelmap/wheelmap.tester.js b/services/wheelmap/wheelmap.tester.js
deleted file mode 100644
index 1a618029fd413..0000000000000
--- a/services/wheelmap/wheelmap.tester.js
+++ /dev/null
@@ -1,44 +0,0 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
-const { noToken } = require('../test-helpers')
-const noWheelmapToken = noToken(require('./wheelmap.service'))
-
-t.create('node with accessibility')
- .skipWhen(noWheelmapToken)
- .get('/26699541.json')
- .timeout(7500)
- .expectBadge({
- label: 'accessibility',
- message: 'yes',
- color: 'brightgreen',
- })
-
-t.create('node with limited accessibility')
- .skipWhen(noWheelmapToken)
- .get('/2034868974.json')
- .timeout(7500)
- .expectBadge({
- label: 'accessibility',
- message: 'limited',
- color: 'yellow',
- })
-
-t.create('node without accessibility')
- .skipWhen(noWheelmapToken)
- .get('/-147495158.json')
- .timeout(7500)
- .expectBadge({
- label: 'accessibility',
- message: 'no',
- color: 'red',
- })
-
-t.create('node not found')
- .skipWhen(noWheelmapToken)
- .get('/0.json')
- .timeout(7500)
- .expectBadge({
- label: 'accessibility',
- message: 'node not found',
- })
diff --git a/services/wikiapiary/wikiapiary-installs.service.js b/services/wikiapiary/wikiapiary-installs.service.js
new file mode 100644
index 0000000000000..39a284f6b490a
--- /dev/null
+++ b/services/wikiapiary/wikiapiary-installs.service.js
@@ -0,0 +1,11 @@
+import { retiredService } from '../index.js'
+
+export default retiredService({
+ category: 'downloads',
+ route: {
+ base: 'wikiapiary',
+ pattern: ':various+',
+ },
+ label: 'wikiapiary',
+ dateAdded: new Date('2025-11-30'),
+})
diff --git a/services/wikiapiary/wikiapiary-installs.tester.js b/services/wikiapiary/wikiapiary-installs.tester.js
new file mode 100644
index 0000000000000..f6ad7ede9ff16
--- /dev/null
+++ b/services/wikiapiary/wikiapiary-installs.tester.js
@@ -0,0 +1,14 @@
+import { ServiceTester } from '../tester.js'
+
+export const t = new ServiceTester({
+ id: 'wikiapiary',
+ title: 'WikiApiary',
+})
+
+t.create('Extension')
+ .get('/extension/installs/ParserFunctions.json')
+ .expectBadge({ label: 'wikiapiary', message: 'retired badge' })
+
+t.create('Skins')
+ .get('/skin/installs/Vector.json')
+ .expectBadge({ label: 'wikiapiary', message: 'retired badge' })
diff --git a/services/winget/version.js b/services/winget/version.js
new file mode 100644
index 0000000000000..9f7c7763463e5
--- /dev/null
+++ b/services/winget/version.js
@@ -0,0 +1,172 @@
+/**
+ * Comparing versions with winget's version comparator.
+ *
+ * See https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp for original implementation.
+ *
+ * @module
+ */
+
+/**
+ * Compares two strings representing version numbers lexicographically and returns an integer value.
+ *
+ * @param {string} v1 - The first version to compare
+ * @param {string} v2 - The second version to compare
+ * @returns {number} -1 if v1 is smaller than v2, 1 if v1 is larger than v2, 0 if v1 and v2 are equal
+ * @example
+ * compareVersion('1.2.3', '1.2.4') // returns -1 because numeric part of first version is smaller than the numeric part of second version.
+ */
+function compareVersion(v1, v2) {
+ // https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L109-L173
+ // This implementation does not parse s_Approximate_Greater_Than
+ // and s_Approximate_Less_Than since they won't appear in directory name (package version parsed by shields.io)
+ const v1Trimmed = trimPrefix(v1)
+ const v2Trimmed = trimPrefix(v2)
+
+ const v1Latest = v1Trimmed.trim().toLowerCase() === 'latest'
+ const v2Latest = v2Trimmed.trim().toLowerCase() === 'latest'
+
+ if (v1Latest && v2Latest) {
+ return 0
+ } else if (v1Latest) {
+ return 1
+ } else if (v2Latest) {
+ return -1
+ }
+
+ const v1Unknown = v1Trimmed.trim().toLowerCase() === 'unknown'
+ const v2Unknown = v2Trimmed.trim().toLowerCase() === 'unknown'
+
+ if (v1Unknown && v2Unknown) {
+ return 0
+ } else if (v1Unknown) {
+ return -1
+ } else if (v2Unknown) {
+ return 1
+ }
+
+ const parts1 = v1Trimmed.split('.')
+ const parts2 = v2Trimmed.split('.')
+
+ trimLastZeros(parts1)
+ trimLastZeros(parts2)
+
+ for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
+ const part1 = parts1[i]
+ const part2 = parts2[i]
+
+ const compare = compareVersionPart(part1, part2)
+ if (compare !== 0) {
+ return compare
+ }
+ }
+
+ if (parts1.length === parts2.length) {
+ return 0
+ }
+
+ if (parts1.length > parts2.length) {
+ return 1
+ } else if (parts1.length < parts2.length) {
+ return -1
+ }
+
+ return 0
+}
+
+/**
+ * Removes all leading non-digit characters from a version number string
+ * if there is a digit before the split character, or no split characters exist.
+ *
+ * @param {string} version The version number string to trim
+ * @returns {string} The version number string with all leading non-digit characters removed
+ */
+function trimPrefix(version) {
+ // https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L66
+ // If there is a digit before the split character, or no split characters exist, trim off all leading non-digit characters
+
+ const digitPos = version.match(/(\d.*)/)
+ const splitPos = version.match(/\./)
+ if (digitPos && (splitPos == null || digitPos.index < splitPos.index)) {
+ // there is digit before the split character so strip off all leading non-digit characters
+ return version.slice(digitPos.index)
+ }
+ return version
+}
+
+/**
+ * Removes all trailing zeros from a version number part array.
+ *
+ * @param {string[]} parts - parts
+ */
+function trimLastZeros(parts) {
+ while (parts.length > 1 && parts[parts.length - 1].trim() === '0') {
+ parts.pop()
+ }
+}
+
+/**
+ * Compares two strings representing version number parts lexicographically and returns an integer value.
+ *
+ * @param {string} part1 - The first version part to compare
+ * @param {string} part2 - The second version part to compare
+ * @returns {number} -1 if part1 is smaller than part2, 1 if part1 is larger than part2, 0 if part1 and part2 are equal
+ * @example
+ * compareVersionPart('3', '4') // returns -1 because numeric part of first part is smaller than the numeric part of second part.
+ */
+function compareVersionPart(part1, part2) {
+ // https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L324-L352
+ const [, numericString1, other1] = part1.trim().match(/^(\d*)(.*)$/)
+ const [, numericString2, other2] = part2.trim().match(/^(\d*)(.*)$/)
+ const numeric1 = parseInt(numericString1 || '0', 10)
+ const numeric2 = parseInt(numericString2 || '0', 10)
+
+ if (numeric1 < numeric2) {
+ return -1
+ } else if (numeric1 > numeric2) {
+ return 1
+ }
+ // numeric1 === numeric2
+
+ const otherFolded1 = (other1 ?? '').toLowerCase()
+ const otherFolded2 = (other2 ?? '').toLowerCase()
+
+ if (otherFolded1.length !== 0 && otherFolded2.length === 0) {
+ return -1
+ } else if (otherFolded1.length === 0 && otherFolded2.length !== 0) {
+ return 1
+ }
+
+ if (otherFolded1 < otherFolded2) {
+ return -1
+ } else if (otherFolded1 > otherFolded2) {
+ return 1
+ }
+
+ return 0
+}
+
+/**
+ * Finds the largest version number lexicographically from an array of strings representing version numbers and returns it as a string.
+ *
+ * @param {string[]} versions - The array of version numbers to compare
+ * @returns {string|undefined} The largest version number as a string, or undefined if the array is empty
+ * @example
+ * latest(['1.2.3', '1.2.4', '1.3', '2.0']) // returns '2.0' because it is the largest version number.
+ * latest(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta']) // returns '2.0-beta'. there is no special handling for pre-release versions.
+ */
+function latest(versions) {
+ const len = versions.length
+ if (len === 0) {
+ return
+ }
+
+ let version = versions[0]
+ for (let i = 1; i < len; i++) {
+ if (compareVersion(version, versions[i]) <= 0) {
+ version = versions[i]
+ }
+ }
+ return version
+}
+
+export { latest, compareVersion }
diff --git a/services/winget/version.spec.js b/services/winget/version.spec.js
new file mode 100644
index 0000000000000..d4449b8767585
--- /dev/null
+++ b/services/winget/version.spec.js
@@ -0,0 +1,57 @@
+import { test, given } from 'sazerac'
+import { compareVersion, latest } from './version.js'
+
+describe('Winget Version helpers', function () {
+ test(compareVersion, () => {
+ // basic compare
+ // https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L147
+ given('1', '2').expect(-1)
+ given('1.0.0', '2.0.0').expect(-1)
+ given('0.0.1', '0.0.2').expect(-1)
+ given('0.0.1-alpha', '0.0.2-alpha').expect(-1)
+ given('0.0.1-beta', '0.0.2-alpha').expect(-1)
+ given('0.0.1-beta', '0.0.2-alpha').expect(-1)
+ given('13.9.8', '14.1').expect(-1)
+
+ given('1.0', '1.0.0').expect(0)
+
+ // Ensure whitespace doesn't affect equality
+ given('1.0', '1.0 ').expect(0)
+ given('1.0', '1. 0').expect(0)
+
+ // Ensure versions with preambles are sorted correctly
+ given('1.0', 'Version 1.0').expect(0)
+ given('foo1', 'bar1').expect(0)
+ given('v0.0.1', '0.0.2').expect(-1)
+ given('v0.0.1', 'v0.0.2').expect(-1)
+ given('1.a2', '1.b1').expect(-1)
+ given('alpha', 'beta').expect(-1)
+
+ // latest
+ // https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L217
+ given('1.0', 'latest').expect(-1)
+ given('100', 'latest').expect(-1)
+ given('943849587389754876.1', 'latest').expect(-1)
+ given('latest', 'LATEST').expect(0)
+
+ // unknown
+ // https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L231
+ given('unknown', '1.0').expect(-1)
+ given('unknown', '1.fork').expect(-1)
+ given('unknown', 'UNKNOWN').expect(0)
+
+ // porting failure tests
+ // https://github.com/badges/shields/pull/10245#discussion_r1817931237
+ // trailing .0 and .0-beta
+ given('1.6.0', '1.6.0-beta.98').expect(-1)
+ })
+
+ test(latest, () => {
+ given(['1.2.3', '1.2.4', '2.0', '1.3.9.1']).expect('2.0')
+ given(['1.2.3', '1.2.4', '2.0-beta', '1.3-alpha']).expect('2.0-beta')
+
+ // compareVersion('3.1.1.0', '3.1.1') == 0, so It's free to choose any of them.
+ // I don't know why but it looks winget registry uses last newest version.
+ given(['3.1.1.0', '3.1.1']).expect('3.1.1')
+ })
+})
diff --git a/services/winget/winget-base.js b/services/winget/winget-base.js
new file mode 100644
index 0000000000000..52732a1cfb8e5
--- /dev/null
+++ b/services/winget/winget-base.js
@@ -0,0 +1,127 @@
+import Joi from 'joi'
+import gql from 'graphql-tag'
+import { InvalidParameter } from '../index.js'
+import { GithubAuthV4Service } from '../github/github-auth-service.js'
+import { transformErrors } from '../github/github-helpers.js'
+import { latest } from './version.js'
+
+const directorySchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ object: Joi.object({
+ entries: Joi.array().items(
+ Joi.object({
+ type: Joi.string().required(),
+ name: Joi.string().required(),
+ object: Joi.object({
+ entries: Joi.array().items(
+ Joi.object({
+ type: Joi.string().required(),
+ name: Joi.string().required(),
+ }),
+ ),
+ }).required(),
+ }),
+ ),
+ })
+ .allow(null)
+ .required(),
+ }).required(),
+ }).required(),
+}).required()
+
+const manifestSchema = Joi.object({
+ data: Joi.object({
+ repository: Joi.object({
+ object: Joi.object({
+ text: Joi.string().required(),
+ }).allow(null),
+ }).required(),
+ }).required(),
+}).required()
+
+export default class WingetBase extends GithubAuthV4Service {
+ static defaultBadgeData = {
+ label: 'winget',
+ }
+
+ static manifestPathFor({ name, version }) {
+ const nameFirstLower = name[0].toLowerCase()
+ const nameSlashed = name.replaceAll('.', '/')
+ return `manifests/${nameFirstLower}/${nameSlashed}/${version}`
+ }
+
+ // Returns GraphQL response for the package's version directory listing.
+ async fetchVersions({ name }) {
+ const nameFirstLower = name[0].toLowerCase()
+ const nameSlashed = name.replaceAll('.', '/')
+ const expression = `HEAD:manifests/${nameFirstLower}/${nameSlashed}`
+ return this._requestGraphql({
+ query: gql`
+ query RepoFiles($expression: String!) {
+ repository(owner: "microsoft", name: "winget-pkgs") {
+ object(expression: $expression) {
+ ... on Tree {
+ entries {
+ type
+ name
+ object {
+ ... on Tree {
+ entries {
+ type
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ `,
+ variables: { expression },
+ schema: directorySchema,
+ transformErrors,
+ })
+ }
+
+ // Returns the text content of a single manifest file at the given expression.
+ async fetchManifest({ expression }) {
+ return this._requestGraphql({
+ query: gql`
+ query Manifest($expression: String!) {
+ repository(owner: "microsoft", name: "winget-pkgs") {
+ object(expression: $expression) {
+ ... on Blob {
+ text
+ }
+ }
+ }
+ }
+ `,
+ variables: { expression },
+ schema: manifestSchema,
+ transformErrors,
+ })
+ }
+
+ // Returns the latest version string for a package, or throws if not found.
+ async getLatestVersion({ name }) {
+ const json = await this.fetchVersions({ name })
+ if (json.data.repository.object?.entries == null) {
+ throw new InvalidParameter({ prettyMessage: 'package not found' })
+ }
+ const entries = json.data.repository.object.entries
+ const directories = entries.filter(entry => entry.type === 'tree')
+ const versionDirs = directories.filter(dir =>
+ dir.object.entries.some(
+ file => file.type === 'blob' && file.name === `${name}.yaml`,
+ ),
+ )
+ const version = latest(versionDirs.map(dir => dir.name))
+ if (version == null) {
+ throw new InvalidParameter({ prettyMessage: 'no versions found' })
+ }
+ return version
+ }
+}
diff --git a/services/winget/winget-release-date.service.js b/services/winget/winget-release-date.service.js
new file mode 100644
index 0000000000000..2aa0bc59466fb
--- /dev/null
+++ b/services/winget/winget-release-date.service.js
@@ -0,0 +1,58 @@
+import * as yaml from 'js-yaml'
+import { renderDateBadge } from '../date.js'
+import { pathParam, InvalidResponse } from '../index.js'
+import WingetBase from './winget-base.js'
+
+export default class WingetReleaseDate extends WingetBase {
+ static category = 'activity'
+
+ static route = {
+ base: 'winget',
+ pattern: 'release-date/:name',
+ }
+
+ static openApi = {
+ '/winget/release-date/{name}': {
+ get: {
+ summary: 'WinGet Package Release Date',
+ description: 'WinGet Community Repository',
+ parameters: [
+ pathParam({
+ name: 'name',
+ example: 'Microsoft.WSL',
+ }),
+ ],
+ },
+ },
+ }
+
+ static defaultBadgeData = {
+ label: 'release date',
+ }
+
+ async handle({ name }) {
+ const version = await this.getLatestVersion({ name })
+ const manifestPath = this.constructor.manifestPathFor({ name, version })
+ const filesToTry = [`${name}.installer.yaml`, `${name}.yaml`]
+
+ let releaseDate
+ for (const file of filesToTry) {
+ const json = await this.fetchManifest({
+ expression: `HEAD:${manifestPath}/${file}`,
+ })
+ const text = json.data.repository.object?.text
+ if (!text) continue
+ const manifest = yaml.load(text)
+ if (manifest?.ReleaseDate) {
+ releaseDate = manifest.ReleaseDate
+ break
+ }
+ }
+
+ if (!releaseDate) {
+ throw new InvalidResponse({ prettyMessage: 'no release date found' })
+ }
+
+ return renderDateBadge(releaseDate)
+ }
+}
diff --git a/services/winget/winget-release-date.tester.js b/services/winget/winget-release-date.tester.js
new file mode 100644
index 0000000000000..732448a76a2b0
--- /dev/null
+++ b/services/winget/winget-release-date.tester.js
@@ -0,0 +1,124 @@
+import { isFormattedDate } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+// minimal winget-package tree/manifest fixtures
+function packageVersionTree(version, packageName) {
+ return {
+ type: 'tree',
+ name: version,
+ object: {
+ entries: [{ type: 'blob', name: `${packageName}.yaml` }],
+ },
+ }
+}
+
+function manifestText(packageName, nReleaseDate) {
+ return `PackageIdentifier: ${packageName}\nReleaseDate: ${nReleaseDate}\n`
+}
+
+// basic test
+t.create('release date for WSL')
+ .get('/release-date/Microsoft.WSL.json')
+ .expectBadge({ label: 'release date', message: isFormattedDate })
+
+// 0.200.170 would sort higher in ASCII, 0.1201.422.0 is latest in winget order
+t.create('gets release date for latest version (not ASCII order)')
+ .intercept(nock =>
+ nock('https://api.github.com/')
+ .post('/graphql')
+ .twice()
+ .reply((uri, requestBody) => {
+ const { query, variables } = JSON.parse(requestBody)
+ if (query.includes('Tree')) {
+ return [
+ 200,
+ {
+ data: {
+ repository: {
+ object: {
+ entries: [
+ packageVersionTree('0.200.170.0', 'Microsoft.DevHome'),
+ packageVersionTree('0.1201.442.0', 'Microsoft.DevHome'),
+ ],
+ },
+ },
+ },
+ },
+ ]
+ }
+
+ if (variables.expression.includes('0.1201.442.0')) {
+ return [
+ 200,
+ {
+ data: {
+ repository: {
+ object: {
+ text: manifestText('Microsoft.DevHome', '2024-03-15'),
+ },
+ },
+ },
+ },
+ ]
+ }
+
+ return [200, { data: { repository: { object: null } } }]
+ }),
+ )
+ .get('/release-date/Microsoft.DevHome.json')
+ .expectBadge({ label: 'release date', message: 'march 2024' })
+
+// 2404 is Ubuntu 24.04 sub-package folder, NOT canonical Ubuntu 24.04
+t.create('do not pick sub-package release date')
+ .intercept(nock =>
+ nock('https://api.github.com/')
+ .post('/graphql')
+ .twice()
+ .reply((uri, requestBody) => {
+ const { query, variables } = JSON.parse(requestBody)
+
+ if (query.includes('Tree')) {
+ return [
+ 200,
+ {
+ data: {
+ repository: {
+ object: {
+ entries: [
+ {
+ type: 'tree',
+ name: '2404',
+ object: {
+ entries: [{ type: 'tree', name: '2404.0.5.0' }],
+ },
+ },
+ packageVersionTree('2204.1.8.0', 'Canonical.Ubuntu'),
+ ],
+ },
+ },
+ },
+ },
+ ]
+ }
+
+ if (variables.expression.includes('2204.1.8.0')) {
+ return [
+ 200,
+ {
+ data: {
+ repository: {
+ object: {
+ text: manifestText('Canonical.Ubuntu', '2024-03-15'),
+ },
+ },
+ },
+ },
+ ]
+ }
+ return [200, { data: { repository: { object: null } } }]
+ }),
+ )
+ .get('/release-date/Canonical.Ubuntu.json')
+ .expectBadge({ label: 'release date', message: 'march 2024' })
diff --git a/services/winget/winget-version.service.js b/services/winget/winget-version.service.js
new file mode 100644
index 0000000000000..8b5105b631e3f
--- /dev/null
+++ b/services/winget/winget-version.service.js
@@ -0,0 +1,32 @@
+import { renderVersionBadge } from '../version.js'
+import { pathParam } from '../index.js'
+import WingetBase from './winget-base.js'
+
+export default class WingetVersion extends WingetBase {
+ static category = 'version'
+
+ static route = {
+ base: 'winget/v',
+ pattern: ':name',
+ }
+
+ static openApi = {
+ '/winget/v/{name}': {
+ get: {
+ summary: 'WinGet Package Version',
+ description: 'WinGet Community Repository',
+ parameters: [
+ pathParam({
+ name: 'name',
+ example: 'Microsoft.WSL',
+ }),
+ ],
+ },
+ },
+ }
+
+ async handle({ name }) {
+ const version = await this.getLatestVersion({ name })
+ return renderVersionBadge({ version })
+ }
+}
diff --git a/services/winget/winget-version.tester.js b/services/winget/winget-version.tester.js
new file mode 100644
index 0000000000000..0bc5bf6c6bf1c
--- /dev/null
+++ b/services/winget/winget-version.tester.js
@@ -0,0 +1,343 @@
+import { isVPlusDottedVersionNClauses } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+
+export const t = await createServiceTester()
+
+// basic test
+t.create('gets the package version of WSL')
+ .get('/Microsoft.WSL.json')
+ .expectBadge({ label: 'winget', message: isVPlusDottedVersionNClauses })
+
+// test more than one dots
+t.create('gets the package version of .NET 8')
+ .get('/Microsoft.DotNet.SDK.8.json')
+ .expectBadge({ label: 'winget', message: isVPlusDottedVersionNClauses })
+
+// test sort based on dotted version order instead of ASCII
+t.create('gets the latest version')
+ .intercept(nock =>
+ nock('https://api.github.com/')
+ .post('/graphql')
+ .reply(200, {
+ data: {
+ repository: {
+ object: {
+ entries: [
+ {
+ type: 'tree',
+ name: '0.1001.389.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.1101.416.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.1201.442.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.137.141.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.200.170.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.503.261.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.601.285.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.601.297.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.701.323.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '0.801.344.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Microsoft.DevHome.yaml',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ }),
+ )
+ .get('/Microsoft.DevHome.json')
+ .expectBadge({ label: 'winget', message: 'v0.1201.442.0' })
+
+// Both 'Some.Package' and 'Some.Package.Sub' are present in the response.
+// We should ignore 'Some.Package.Sub' in response to 'Some.Package' request.
+// In this test case, Canonical.Ubuntu.2404 is present, but it should not be treated as Canonical.Ubuntu version 2404.
+t.create('do not pick sub-package as version')
+ .intercept(nock =>
+ nock('https://api.github.com/')
+ .post('/graphql')
+ .reply(200, {
+ data: {
+ repository: {
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: '.validation',
+ object: {},
+ },
+ {
+ type: 'tree',
+ name: '1804',
+ object: {
+ entries: [
+ {
+ type: 'tree',
+ name: '1804.6.4.0',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '2004',
+ object: {
+ entries: [
+ {
+ type: 'tree',
+ name: '2004.6.16.0',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '2204.1.8.0',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: 'Canonical.Ubuntu.installer.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Canonical.Ubuntu.locale.en-US.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Canonical.Ubuntu.locale.zh-CN.yaml',
+ },
+ {
+ type: 'blob',
+ name: 'Canonical.Ubuntu.yaml',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '2204',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: '.validation',
+ },
+ {
+ type: 'tree',
+ name: '2204.0.10.0',
+ },
+ {
+ type: 'tree',
+ name: '2204.2.47.0',
+ },
+ ],
+ },
+ },
+ {
+ type: 'tree',
+ name: '2404',
+ object: {
+ entries: [
+ {
+ type: 'blob',
+ name: '.validation',
+ },
+ {
+ type: 'tree',
+ name: '2404.0.5.0',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ }),
+ )
+ .get('/Canonical.Ubuntu.json')
+ .expectBadge({ label: 'winget', message: 'v2204.1.8.0' })
diff --git a/services/wordpress/wordpress-base.js b/services/wordpress/wordpress-base.js
index 567cf72614972..07d2c45dd9e3d 100644
--- a/services/wordpress/wordpress-base.js
+++ b/services/wordpress/wordpress-base.js
@@ -1,53 +1,93 @@
-'use strict'
+import Joi from 'joi'
+import qs from 'qs'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, NotFound } from '../index.js'
-const Joi = require('@hapi/joi')
-const { nonNegativeInteger } = require('../validators')
-const { BaseJsonService, NotFound } = require('..')
+const stringOrFalse = Joi.alternatives(Joi.string(), Joi.bool())
-const foundSchema = Joi.object()
+const themeSchema = Joi.object()
.keys({
- version: Joi.string(),
+ version: Joi.string().required(),
rating: nonNegativeInteger,
num_ratings: nonNegativeInteger,
downloaded: nonNegativeInteger,
active_installs: nonNegativeInteger,
- requires: Joi.string(), // Plugin Only
- tested: Joi.string(), // Plugin Only
+ last_updated: Joi.string().required(),
+ requires_php: stringOrFalse.required(),
+ requires: stringOrFalse.required(),
})
.required()
-const notFoundSchema = Joi.string().allow(null, false)
+const pluginSchema = Joi.object()
+ .keys({
+ version: Joi.string().required(),
+ rating: nonNegativeInteger,
+ num_ratings: nonNegativeInteger,
+ downloaded: nonNegativeInteger,
+ active_installs: nonNegativeInteger,
+ requires: stringOrFalse.required(),
+ tested: Joi.string().required(),
+ last_updated: Joi.string().required(),
+ requires_php: stringOrFalse.required(),
+ })
+ .required()
+
+const notFoundSchema = Joi.object()
+ .keys({
+ error: Joi.string().required(),
+ })
+ .required()
-const schemas = Joi.alternatives(foundSchema, notFoundSchema)
+const pluginSchemas = Joi.alternatives(pluginSchema, notFoundSchema)
+const themeSchemas = Joi.alternatives(themeSchema, notFoundSchema)
-module.exports = class BaseWordpress extends BaseJsonService {
+export class BaseWordpress extends BaseJsonService {
async fetch({ extensionType, slug }) {
- const url = `https://api.wordpress.org/${extensionType}s/info/1.1/`
+ const url = `https://api.wordpress.org/${extensionType}s/info/1.2/`
+ let schemas
+ if (extensionType === 'plugin') {
+ schemas = pluginSchemas
+ } else if (extensionType === 'theme') {
+ schemas = themeSchemas
+ }
+
+ const queryString = qs.stringify(
+ {
+ action: `${extensionType}_information`,
+ request: {
+ slug,
+ fields: {
+ active_installs: 1,
+ sections: 0,
+ homepage: 0,
+ tags: 0,
+ screenshot_url: 0,
+ downloaded: 1,
+ last_updated: 1,
+ requires_php: 1,
+ },
+ },
+ },
+ { encode: false },
+ )
+
const json = await this._requestJson({
url,
schema: schemas,
options: {
- qs: {
- action: `${extensionType}_information`,
- request: {
- slug,
- fields: {
- active_installs: 1,
- sections: 0,
- homepage: 0,
- tags: 0,
- screenshot_url: 0,
- },
- },
- },
- qsStringifyOptions: {
- encode: false,
- },
+ searchParams: queryString,
},
})
- if (!json) {
+ if ('error' in json) {
throw new NotFound()
}
return json
}
}
+
+export const description = `
+These badges rely on an API that is no longer supported by Wordpress. You are
+still free to use them, simply bear in mind that Shields.io cannot guarantee
+that they'll keep on working in the future. Please also double-check the
+provided slug, as an incorrect value may lead to unexpected results.
+`
diff --git a/services/wordpress/wordpress-downloads.service.js b/services/wordpress/wordpress-downloads.service.js
index e1edfa0886fba..cd01f34575127 100644
--- a/services/wordpress/wordpress-downloads.service.js
+++ b/services/wordpress/wordpress-downloads.service.js
@@ -1,10 +1,7 @@
-'use strict'
-
-const Joi = require('@hapi/joi')
-const { metric } = require('../text-formatters')
-const { downloadCount } = require('../color-formatters')
-const { NotFound } = require('..')
-const BaseWordpress = require('./wordpress-base')
+import Joi from 'joi'
+import { renderDownloadsBadge } from '../downloads.js'
+import { pathParams } from '../index.js'
+import { description, BaseWordpress } from './wordpress-base.js'
const dateSchema = Joi.object()
.pattern(Joi.date().iso(), Joi.number().integer())
@@ -24,23 +21,22 @@ const extensionData = {
const intervalMap = {
dd: {
limit: 1,
- messageSuffix: '/day',
+ interval: 'day',
},
dw: {
limit: 7,
- messageSuffix: '/week',
+ interval: 'week',
},
dm: {
limit: 30,
- messageSuffix: '/month',
+ interval: 'month',
},
dy: {
limit: 365,
- messageSuffix: '/year',
+ interval: 'year',
},
dt: {
limit: null,
- messageSuffix: '',
},
}
@@ -48,42 +44,46 @@ function DownloadsForExtensionType(extensionType) {
const { capt, exampleSlug } = extensionData[extensionType]
return class WordpressDownloads extends BaseWordpress {
- static get name() {
- return `Wordpress${capt}Downloads`
- }
+ static name = `Wordpress${capt}Downloads`
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get route() {
- return {
- base: `wordpress/${extensionType}`,
- pattern: ':interval(dd|dw|dm|dy|dt)/:slug',
- }
+ static route = {
+ base: `wordpress/${extensionType}`,
+ pattern: ':interval(dd|dw|dm|dy|dt)/:slug',
}
- static get examples() {
- return [
- {
- title: `WordPress ${capt} Downloads`,
- namedParams: { interval: 'dm', slug: exampleSlug },
- staticPreview: this.render({ interval: 'dm', downloads: 200000 }),
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/{interval}/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Downloads`,
+ description,
+ parameters: pathParams(
+ {
+ name: 'interval',
+ example: 'dm',
+ schema: { type: 'string', enum: this.getEnum('interval') },
+ description: 'Daily, Weekly, Monthly, Yearly, or Total downloads',
+ },
+ {
+ name: 'slug',
+ example: exampleSlug,
+ },
+ ),
},
- ]
+ }
+ return route
}
- static get defaultBadgeData() {
- return { label: 'downloads' }
- }
+ static defaultBadgeData = { label: 'downloads' }
static render({ interval, downloads }) {
- const { messageSuffix } = intervalMap[interval]
-
- return {
- message: `${metric(downloads)}${messageSuffix}`,
- color: downloadCount(downloads),
- }
+ return renderDownloadsBadge({
+ downloads,
+ interval: intervalMap[interval].interval,
+ })
}
async handle({ interval, slug }) {
@@ -96,28 +96,20 @@ function DownloadsForExtensionType(extensionType) {
})
downloads = _downloads
} else {
- const ext_type = extensionType === 'plugin' ? 'plugin' : 'themes'
+ const extType = extensionType === 'plugin' ? 'plugin' : 'themes'
const json = await this._requestJson({
schema: dateSchema,
- url: `https://api.wordpress.org/stats/${ext_type}/1.0/downloads.php`,
+ url: `https://api.wordpress.org/stats/${extType}/1.0/downloads.php`,
options: {
- qs: {
+ searchParams: {
slug,
limit,
},
},
})
- const size = Object.keys(json).length
downloads = Object.values(json).reduce(
- (a, b) => parseInt(a) + parseInt(b)
+ (a, b) => parseInt(a) + parseInt(b),
)
- // This check is for non-existent and brand-new plugins both having new stats.
- // Non-Existent plugins results are the same as a brandspanking new plugin with no downloads.
- if (downloads <= 0 && size <= 1) {
- throw new NotFound({
- prettyMessage: `${extensionType} not found or too new`,
- })
- }
}
return this.constructor.render({ interval, downloads })
@@ -129,48 +121,39 @@ function InstallsForExtensionType(extensionType) {
const { capt, exampleSlug } = extensionData[extensionType]
return class WordpressInstalls extends BaseWordpress {
- static get name() {
- return `Wordpress${capt}Installs`
- }
+ static name = `Wordpress${capt}Installs`
- static get category() {
- return 'downloads'
- }
+ static category = 'downloads'
- static get route() {
- return {
- base: `wordpress/${extensionType}/installs`,
- pattern: ':slug',
- }
+ static route = {
+ base: `wordpress/${extensionType}/installs`,
+ pattern: ':slug',
}
- static get examples() {
- return [
- {
- title: `WordPress ${capt} Active Installs`,
- namedParams: { slug: exampleSlug },
- staticPreview: this.render({ installCount: 300000 }),
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/installs/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Active Installs`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
+ }),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: 'active installs' }
- }
-
- static render({ installCount }) {
- return {
- message: metric(installCount),
- color: downloadCount(installCount),
}
+ return route
}
+ static defaultBadgeData = { label: 'active installs' }
+
async handle({ slug }) {
const { active_installs: installCount } = await this.fetch({
extensionType,
slug,
})
- return this.constructor.render({ installCount })
+ return renderDownloadsBadge({ downloads: installCount })
}
}
}
@@ -178,4 +161,4 @@ function InstallsForExtensionType(extensionType) {
const downloadServices = ['plugin', 'theme'].map(DownloadsForExtensionType)
const installServices = ['plugin', 'theme'].map(InstallsForExtensionType)
const modules = [...downloadServices, ...installServices]
-module.exports = modules
+export default modules
diff --git a/services/wordpress/wordpress-downloads.tester.js b/services/wordpress/wordpress-downloads.tester.js
index a455dbc8aea75..b82bffc9dc949 100644
--- a/services/wordpress/wordpress-downloads.tester.js
+++ b/services/wordpress/wordpress-downloads.tester.js
@@ -1,13 +1,10 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
-
-const t = new ServiceTester({
+export const t = new ServiceTester({
id: 'wordpress',
title: 'WordPress Download Tests',
})
-module.exports = t
t.create('Plugin Downloads - Total')
.get('/plugin/dt/akismet.json')
@@ -23,19 +20,15 @@ t.create('Plugin Downloads - Active')
message: isMetric,
})
-t.create('Plugin Downloads - Day')
- .get('/plugin/dd/akismet.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('Plugin Downloads - Day').get('/plugin/dd/akismet.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
-t.create('Plugin Downloads - Week')
- .get('/plugin/dw/akismet.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('Plugin Downloads - Week').get('/plugin/dw/akismet.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
t.create('Plugin Downloads - Month')
.get('/plugin/dm/akismet.json')
@@ -44,12 +37,10 @@ t.create('Plugin Downloads - Month')
message: isMetricOverTimePeriod,
})
-t.create('Plugin Downloads - Year')
- .get('/plugin/dy/akismet.json')
- .expectBadge({
- label: 'downloads',
- message: isMetricOverTimePeriod,
- })
+t.create('Plugin Downloads - Year').get('/plugin/dy/akismet.json').expectBadge({
+ label: 'downloads',
+ message: isMetricOverTimePeriod,
+})
t.create('Theme Downloads - Total')
.get('/theme/dt/twentyseventeen.json')
@@ -107,34 +98,6 @@ t.create('Plugin Downloads - Active | Not Found')
message: 'not found',
})
-t.create('Plugin Downloads - Day | Not Found')
- .get('/plugin/dd/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'plugin not found or too new',
- })
-
-t.create('Plugin Downloads - Week | Not Found')
- .get('/plugin/dw/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'plugin not found or too new',
- })
-
-t.create('Plugin Downloads - Month | Not Found')
- .get('/plugin/dm/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'plugin not found or too new',
- })
-
-t.create('Plugin Downloads - Year | Not Found')
- .get('/plugin/dy/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'plugin not found or too new',
- })
-
t.create('Theme Downloads - Total | Not Found')
.get('/theme/dt/100.json')
.expectBadge({
@@ -148,31 +111,3 @@ t.create('Theme Downloads - Active | Not Found')
label: 'active installs',
message: 'not found',
})
-
-t.create('Theme Downloads - Day | Not Found')
- .get('/theme/dd/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'theme not found or too new',
- })
-
-t.create('Theme Downloads - Week | Not Found')
- .get('/theme/dw/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'theme not found or too new',
- })
-
-t.create('Theme Downloads - Month | Not Found')
- .get('/theme/dm/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'theme not found or too new',
- })
-
-t.create('Theme Downloads - Year | Not Found')
- .get('/theme/dy/100.json')
- .expectBadge({
- label: 'downloads',
- message: 'theme not found or too new',
- })
diff --git a/services/wordpress/wordpress-last-update.service.js b/services/wordpress/wordpress-last-update.service.js
new file mode 100644
index 0000000000000..9543a3b8d0069
--- /dev/null
+++ b/services/wordpress/wordpress-last-update.service.js
@@ -0,0 +1,63 @@
+import { pathParams } from '../index.js'
+import { parseDate, renderDateBadge } from '../date.js'
+import { description, BaseWordpress } from './wordpress-base.js'
+
+const extensionData = {
+ plugin: {
+ capt: 'Plugin',
+ exampleSlug: 'bbpress',
+ lastUpdateFormat: 'YYYY-MM-DD h:mma [GMT]',
+ },
+ theme: {
+ capt: 'Theme',
+ exampleSlug: 'twentyseventeen',
+ lastUpdateFormat: 'YYYY-MM-DD',
+ },
+}
+
+function LastUpdateForType(extensionType) {
+ const { capt, exampleSlug, lastUpdateFormat } = extensionData[extensionType]
+
+ return class WordpressLastUpdate extends BaseWordpress {
+ static name = `Wordpress${capt}LastUpdated`
+
+ static category = 'activity'
+
+ static route = {
+ base: `wordpress/${extensionType}/last-updated`,
+ pattern: ':slug',
+ }
+
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/last-updated/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Last Updated`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
+ }),
+ },
+ }
+ return route
+ }
+
+ static defaultBadgeData = { label: 'last updated' }
+
+ async handle({ slug }) {
+ const { last_updated: lastUpdated } = await this.fetch({
+ extensionType,
+ slug,
+ })
+
+ const date = parseDate(lastUpdated, lastUpdateFormat)
+
+ return renderDateBadge(date)
+ }
+ }
+}
+
+const lastupdate = ['plugin', 'theme'].map(LastUpdateForType)
+export default [...lastupdate]
diff --git a/services/wordpress/wordpress-last-update.tester.js b/services/wordpress/wordpress-last-update.tester.js
new file mode 100644
index 0000000000000..d490b6ee72f03
--- /dev/null
+++ b/services/wordpress/wordpress-last-update.tester.js
@@ -0,0 +1,35 @@
+import { ServiceTester } from '../tester.js'
+import { isFormattedDate } from '../test-validators.js'
+
+export const t = new ServiceTester({
+ id: 'wordpress',
+ title: 'WordPress Last Update',
+})
+
+t.create('Plugin Last Update')
+ .get('/plugin/last-updated/akismet.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('Plugin (Not Found)')
+ .get('/plugin/last-updated/pleasenevermakethisplugin.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'not found',
+ })
+
+t.create('Theme Last Update')
+ .get('/theme/last-updated/twentytwenty.json')
+ .expectBadge({
+ label: 'last updated',
+ message: isFormattedDate,
+ })
+
+t.create('Theme (Not Found)')
+ .get('/theme/last-updated/pleasenevermakethistheme.json')
+ .expectBadge({
+ label: 'last updated',
+ message: 'not found',
+ })
diff --git a/services/wordpress/wordpress-platform-redirect.service.js b/services/wordpress/wordpress-platform-redirect.service.js
index 10b74f8d99893..5f9544e5e1bcf 100644
--- a/services/wordpress/wordpress-platform-redirect.service.js
+++ b/services/wordpress/wordpress-platform-redirect.service.js
@@ -1,8 +1,6 @@
-'use strict'
+import { redirector } from '../index.js'
-const { redirector } = require('..')
-
-module.exports = redirector({
+export default redirector({
category: 'platform-support',
route: {
base: 'wordpress/v',
diff --git a/services/wordpress/wordpress-platform-redirect.tester.js b/services/wordpress/wordpress-platform-redirect.tester.js
index 2e665cadb9266..2f0816dea5633 100644
--- a/services/wordpress/wordpress-platform-redirect.tester.js
+++ b/services/wordpress/wordpress-platform-redirect.tester.js
@@ -1,6 +1,5 @@
-'use strict'
-
-const t = (module.exports = require('../tester').createServiceTester())
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
t.create('Plugin Tested WP Version (Alias)')
.get('/akismet.svg')
diff --git a/services/wordpress/wordpress-platform.service.js b/services/wordpress/wordpress-platform.service.js
index eb8273c2a5d5d..d3f859e529cf4 100644
--- a/services/wordpress/wordpress-platform.service.js
+++ b/services/wordpress/wordpress-platform.service.js
@@ -1,109 +1,157 @@
-'use strict'
+import { NotFound, pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { description, BaseWordpress } from './wordpress-base.js'
+import { versionColorForWordpressVersion } from './wordpress-version-color.js'
-const { addv } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const BaseWordpress = require('./wordpress-base')
-const { versionColorForWordpressVersion } = require('./wordpress-version-color')
+const extensionData = {
+ plugin: {
+ capt: 'Plugin',
+ exampleSlug: 'bbpress',
+ },
+ theme: {
+ capt: 'Theme',
+ exampleSlug: 'twentytwenty',
+ },
+}
-class WordpressPluginRequiresVersion extends BaseWordpress {
- static get category() {
- return 'platform-support'
- }
+function WordpressRequiresVersion(extensionType) {
+ const { capt, exampleSlug } = extensionData[extensionType]
+
+ return class WordpressRequiresVersion extends BaseWordpress {
+ static name = `Wordpress${capt}RequiresVersion`
- static get route() {
- return {
- base: `wordpress/plugin/wp-version`,
+ static category = 'platform-support'
+
+ static route = {
+ base: `wordpress/${extensionType}/wp-version`,
pattern: ':slug',
}
- }
- static get examples() {
- return [
- {
- title: 'WordPress Plugin: Required WP Version',
- namedParams: { slug: 'bbpress' },
- staticPreview: this.render({ wordpressVersion: '4.8' }),
- },
- ]
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/wp-version/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt}: Required WP Version`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
+ }),
+ },
+ }
+ return route
+ }
+
+ static defaultBadgeData = { label: 'wordpress' }
+
+ async handle({ slug }) {
+ const { requires: wordpressVersion } = await this.fetch({
+ extensionType,
+ slug,
+ })
+
+ if (wordpressVersion === false) {
+ throw new NotFound({
+ prettyMessage: `not set for this ${extensionType}`,
+ })
+ }
+
+ return renderVersionBadge({ version: wordpressVersion })
+ }
}
+}
- static get defaultBadgeData() {
- return { label: 'wordpress' }
+class WordpressPluginTestedVersion extends BaseWordpress {
+ static category = 'platform-support'
+
+ static route = {
+ base: 'wordpress/plugin/tested',
+ pattern: ':slug',
}
- static render({ wordpressVersion }) {
- return {
- message: addv(wordpressVersion),
- color: versionColor(wordpressVersion),
- }
+ static openApi = {
+ '/wordpress/plugin/tested/{slug}': {
+ get: {
+ summary: 'WordPress Plugin: Tested WP Version',
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: 'bbpress',
+ }),
+ },
+ },
}
+ static defaultBadgeData = { label: 'wordpress' }
+
async handle({ slug }) {
- const { requires: wordpressVersion } = await this.fetch({
+ const { tested: testedVersion } = await this.fetch({
extensionType: 'plugin',
slug,
})
- return this.constructor.render({ wordpressVersion })
+ // Atypically, pulling color data from the server with async operation.
+ const color = await versionColorForWordpressVersion(testedVersion)
+ return renderVersionBadge({
+ version: testedVersion,
+ suffix: 'tested',
+ versionFormatter: () => color,
+ })
}
}
-class WordpressPluginTestedVersion extends BaseWordpress {
- static get category() {
- return 'platform-support'
- }
+function RequiresPHPVersionForType(extensionType) {
+ const { capt, exampleSlug } = extensionData[extensionType]
+
+ return class WordpressRequiresPHPVersion extends BaseWordpress {
+ static name = `Wordpress${capt}RequiresPHPVersion`
- static get route() {
- return {
- base: `wordpress/plugin/tested`,
+ static category = 'platform-support'
+
+ static route = {
+ base: `wordpress/${extensionType}/required-php`,
pattern: ':slug',
}
- }
- static get examples() {
- return [
- {
- title: 'WordPress Plugin: Tested WP Version',
- namedParams: { slug: 'bbpress' },
- staticPreview: this.renderStaticPreview({
- testedVersion: '4.9.8',
- }),
- },
- ]
- }
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/required-php/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Required PHP Version`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
+ }),
+ },
+ }
+ return route
+ }
- static get defaultBadgeData() {
- return { label: 'wordpress' }
- }
+ static defaultBadgeData = { label: 'php' }
- static renderStaticPreview({ testedVersion }) {
- // Since this badge has an async `render()` function, but `get examples()` has to
- // be synchronous, this method exists. It should return the same value as the
- // real `render()`.
- return {
- message: `${addv(testedVersion)} tested`,
- color: 'brightgreen',
- }
- }
+ async handle({ slug }) {
+ const { requires_php: requiresPhp } = await this.fetch({
+ extensionType,
+ slug,
+ })
- static async render({ testedVersion }) {
- // Atypically, the `render()` function of this badge is `async` because it needs to pull
- // data from the server.
- return {
- message: `${addv(testedVersion)} tested`,
- color: await versionColorForWordpressVersion(testedVersion),
- }
- }
+ if (requiresPhp === false) {
+ throw new NotFound({
+ prettyMessage: `not set for this ${extensionType}`,
+ })
+ }
- async handle({ slug }) {
- const { tested: testedVersion } = await this.fetch({
- extensionType: 'plugin',
- slug,
- })
- return this.constructor.render({ testedVersion })
+ return renderVersionBadge({ version: requiresPhp, prefix: '>=' })
+ }
}
}
-module.exports = {
- WordpressPluginRequiresVersion,
+const requiredPhp = ['plugin', 'theme'].map(RequiresPHPVersionForType)
+const requiresVersion = ['plugin', 'theme'].map(WordpressRequiresVersion)
+export default [
+ ...requiredPhp,
+ ...requiresVersion,
WordpressPluginTestedVersion,
-}
+]
diff --git a/services/wordpress/wordpress-platform.tester.js b/services/wordpress/wordpress-platform.tester.js
index 312bbe0f86549..118f72e736dbd 100644
--- a/services/wordpress/wordpress-platform.tester.js
+++ b/services/wordpress/wordpress-platform.tester.js
@@ -1,14 +1,15 @@
-'use strict'
+import Joi from 'joi'
+import { ServiceTester } from '../tester.js'
+import {
+ isVPlusDottedVersionAtLeastOne,
+ isComposerVersion,
+} from '../test-validators.js'
-const Joi = require('@hapi/joi')
-const { ServiceTester } = require('../tester')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'WordpressPlatform',
title: 'WordPress Platform Tests',
pathPrefix: '/wordpress',
-}))
+})
t.create('Plugin Required WP Version')
.get('/plugin/wp-version/akismet.json')
@@ -17,6 +18,13 @@ t.create('Plugin Required WP Version')
message: isVPlusDottedVersionAtLeastOne,
})
+t.create('Theme Required WP Version')
+ .get('/theme/wp-version/twentytwenty.json')
+ .expectBadge({
+ label: 'wordpress',
+ message: isVPlusDottedVersionAtLeastOne,
+ })
+
t.create('Plugin Tested WP Version')
.get('/plugin/tested/akismet.json')
.expectBadge({
@@ -24,17 +32,22 @@ t.create('Plugin Tested WP Version')
message: Joi.string().regex(/^v\d+(\.\d+)?(\.\d+)? tested$/),
})
+const mockedQueryFields = {
+ active_installs: '1',
+ sections: '0',
+ homepage: '0',
+ tags: '0',
+ screenshot_url: '0',
+ downloaded: 1,
+ requires_php: 1,
+ last_updated: 1,
+}
+
const mockedQuerySelector = {
action: 'plugin_information',
request: {
slug: 'akismet',
- fields: {
- active_installs: '1',
- sections: '0',
- homepage: '0',
- tags: '0',
- screenshot_url: '0',
- },
+ fields: mockedQueryFields,
},
}
@@ -46,7 +59,7 @@ t.create('Plugin Tested WP Version - current')
.get('/plugin/tested/akismet.json')
.intercept(nock =>
nock('https://api.wordpress.org')
- .get('/plugins/info/1.1/')
+ .get('/plugins/info/1.2/')
.query(mockedQuerySelector)
.reply(200, {
version: '1.3',
@@ -56,9 +69,11 @@ t.create('Plugin Tested WP Version - current')
active_installs: 100,
requires: '4.9',
tested: '4.9.8',
+ last_updated: '2020-01-01 7:21am GMT',
+ requires_php: '5.5',
})
.get('/core/version-check/1.7/')
- .reply(200, mockedCoreResponseData)
+ .reply(200, mockedCoreResponseData),
)
.expectBadge({
label: 'wordpress',
@@ -70,7 +85,7 @@ t.create('Plugin Tested WP Version - old')
.get('/plugin/tested/akismet.json')
.intercept(nock =>
nock('https://api.wordpress.org')
- .get('/plugins/info/1.1/')
+ .get('/plugins/info/1.2/')
.query(mockedQuerySelector)
.reply(200, {
version: '1.2',
@@ -80,9 +95,11 @@ t.create('Plugin Tested WP Version - old')
active_installs: 100,
requires: '4.9',
tested: '4.9.6',
+ last_updated: '2020-01-01 7:21am GMT',
+ requires_php: '5.5',
})
.get('/core/version-check/1.7/')
- .reply(200, mockedCoreResponseData)
+ .reply(200, mockedCoreResponseData),
)
.expectBadge({
label: 'wordpress',
@@ -94,7 +111,7 @@ t.create('Plugin Tested WP Version - non-exsistant or unsupported')
.get('/plugin/tested/akismet.json')
.intercept(nock =>
nock('https://api.wordpress.org')
- .get('/plugins/info/1.1/')
+ .get('/plugins/info/1.2/')
.query(mockedQuerySelector)
.reply(200, {
version: '1.2',
@@ -104,9 +121,11 @@ t.create('Plugin Tested WP Version - non-exsistant or unsupported')
active_installs: 100,
requires: '4.0',
tested: '4.0.0',
+ last_updated: '2020-01-01 7:21am GMT',
+ requires_php: '5.5',
})
.get('/core/version-check/1.7/')
- .reply(200, mockedCoreResponseData)
+ .reply(200, mockedCoreResponseData),
)
.expectBadge({
label: 'wordpress',
@@ -114,6 +133,29 @@ t.create('Plugin Tested WP Version - non-exsistant or unsupported')
color: 'yellow',
})
+t.create('Plugin Required WP Version | Missing')
+ .get('/plugin/wp-version/akismet.json')
+ .intercept(nock =>
+ nock('https://api.wordpress.org')
+ .get('/plugins/info/1.2/')
+ .query(mockedQuerySelector)
+ .reply(200, {
+ version: '1.2',
+ rating: 80,
+ num_ratings: 100,
+ downloaded: 100,
+ active_installs: 100,
+ requires: false,
+ tested: '4.0.0',
+ last_updated: '2020-01-01 7:21am GMT',
+ requires_php: '5.5',
+ }),
+ )
+ .expectBadge({
+ label: 'wordpress',
+ message: 'not set for this plugin',
+ })
+
t.create('Plugin Required WP Version | Not Found')
.get('/plugin/wp-version/100.json')
.expectBadge({
@@ -121,6 +163,13 @@ t.create('Plugin Required WP Version | Not Found')
message: 'not found',
})
+t.create('Theme Required WP Version | Not Found')
+ .get('/theme/wp-version/100.json')
+ .expectBadge({
+ label: 'wordpress',
+ message: 'not found',
+ })
+
t.create('Plugin Tested WP Version | Not Found')
.get('/plugin/tested/100.json')
.expectBadge({
@@ -131,3 +180,69 @@ t.create('Plugin Tested WP Version | Not Found')
t.create('Plugin Tested WP Version (Alias)')
.get('/v/100.svg')
.expectRedirect('/wordpress/plugin/tested/100.svg')
+
+t.create('Plugin Required PHP Version')
+ .get('/plugin/required-php/jetpack.json')
+ .expectBadge({
+ label: 'php',
+ message: isComposerVersion,
+ })
+
+t.create('Plugin Required PHP Version (Not Set)')
+ .get('/plugin/required-php/akismet.json')
+ .intercept(nock =>
+ nock('https://api.wordpress.org')
+ .get('/plugins/info/1.2/')
+ .query(mockedQuerySelector)
+ .reply(200, {
+ version: '1.2',
+ rating: 80,
+ num_ratings: 100,
+ downloaded: 100,
+ active_installs: 100,
+ requires: false,
+ tested: '4.0.0',
+ last_updated: '2020-01-01 7:21am GMT',
+ requires_php: false,
+ }),
+ )
+ .expectBadge({
+ label: 'php',
+ message: 'not set for this plugin',
+ })
+
+t.create('Theme Required PHP Version')
+ .get('/theme/required-php/twentytwenty.json')
+ .expectBadge({
+ label: 'php',
+ message: isComposerVersion,
+ })
+
+t.create('Theme Required PHP Version (Not Set)')
+ .get('/theme/required-php/generatepress.json')
+ .intercept(nock =>
+ nock('https://api.wordpress.org')
+ .get('/themes/info/1.2/')
+ .query({
+ action: 'theme_information',
+ request: {
+ slug: 'generatepress',
+ fields: mockedQueryFields,
+ },
+ })
+ .reply(200, {
+ version: '1.2',
+ rating: 80,
+ num_ratings: 100,
+ downloaded: 100,
+ active_installs: 100,
+ requires: '4.0',
+ tested: '4.0.0',
+ requires_php: false,
+ last_updated: '2020-01-01',
+ }),
+ )
+ .expectBadge({
+ label: 'php',
+ message: 'not set for this theme',
+ })
diff --git a/services/wordpress/wordpress-rating.service.js b/services/wordpress/wordpress-rating.service.js
index d349637a397b2..9c43c00c76351 100644
--- a/services/wordpress/wordpress-rating.service.js
+++ b/services/wordpress/wordpress-rating.service.js
@@ -1,8 +1,7 @@
-'use strict'
-
-const { starRating, metric } = require('../text-formatters')
-const { floorCount } = require('../color-formatters')
-const BaseWordpress = require('./wordpress-base')
+import { floorCount } from '../color-formatters.js'
+import { pathParams } from '../index.js'
+import { starRating, metric } from '../text-formatters.js'
+import { description, BaseWordpress } from './wordpress-base.js'
const extensionData = {
plugin: {
@@ -16,41 +15,36 @@ const extensionData = {
}
class WordpressRatingBase extends BaseWordpress {
- static get category() {
- return 'rating'
- }
+ static category = 'rating'
- static get defaultBadgeData() {
- return { label: 'rating' }
- }
+ static defaultBadgeData = { label: 'rating' }
}
function RatingForExtensionType(extensionType) {
const { capt, exampleSlug } = extensionData[extensionType]
return class WordpressRating extends WordpressRatingBase {
- static get name() {
- return `Wordpress${capt}Rating`
- }
+ static name = `Wordpress${capt}Rating`
- static get route() {
- return {
- base: `wordpress/${extensionType}/rating`,
- pattern: ':slug',
- }
+ static route = {
+ base: `wordpress/${extensionType}/rating`,
+ pattern: ':slug',
}
- static get examples() {
- return [
- {
- title: `WordPress ${capt} Rating`,
- namedParams: { slug: exampleSlug },
- staticPreview: this.render({
- rating: 80,
- numRatings: 100,
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/rating/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Rating`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
}),
},
- ]
+ }
+ return route
}
static render({ rating, numRatings }) {
@@ -75,29 +69,27 @@ function StarsForExtensionType(extensionType) {
const { capt, exampleSlug } = extensionData[extensionType]
return class WordpressStars extends WordpressRatingBase {
- static get name() {
- return `Wordpress${capt}Stars`
- }
+ static name = `Wordpress${capt}Stars`
- static get route() {
- return {
- base: `wordpress/${extensionType}`,
- pattern: '(stars|r)/:slug',
- }
+ static route = {
+ base: `wordpress/${extensionType}`,
+ pattern: '(stars|r)/:slug',
}
- static get examples() {
- return [
- {
- title: `WordPress ${capt} Rating`,
- pattern: 'stars/:slug',
- namedParams: { slug: exampleSlug },
- staticPreview: this.render({
- rating: 80,
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/stars/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Stars`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
}),
- documentation: 'There is an alias /r/:slug.svg as well.',
},
- ]
+ }
+ return route
}
static render({ rating }) {
@@ -119,4 +111,4 @@ const ratingsServices = ['plugin', 'theme'].map(RatingForExtensionType)
const starsServices = ['plugin', 'theme'].map(StarsForExtensionType)
const modules = [...ratingsServices, ...starsServices]
-module.exports = modules
+export default modules
diff --git a/services/wordpress/wordpress-rating.tester.js b/services/wordpress/wordpress-rating.tester.js
index a1e1b8c3bae2b..1c686890f16cf 100644
--- a/services/wordpress/wordpress-rating.tester.js
+++ b/services/wordpress/wordpress-rating.tester.js
@@ -1,13 +1,10 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isStarRating } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isStarRating } = require('../test-validators')
-
-const t = new ServiceTester({
+export const t = new ServiceTester({
id: 'wordpress',
title: 'WordPress Rating Tests',
})
-module.exports = t
t.create('Plugin Rating - Stars')
.get('/plugin/stars/akismet.json')
diff --git a/services/wordpress/wordpress-version-color.integration.js b/services/wordpress/wordpress-version-color.integration.js
new file mode 100644
index 0000000000000..2bbeef5a349bc
--- /dev/null
+++ b/services/wordpress/wordpress-version-color.integration.js
@@ -0,0 +1,23 @@
+import { expect } from 'chai'
+import { versionColorForWordpressVersion } from './wordpress-version-color.js'
+
+describe('versionColorForWordpressVersion()', function () {
+ it('generates correct colours for given versions', async function () {
+ this.timeout(5e3)
+
+ expect(await versionColorForWordpressVersion('11.2.0')).to.equal(
+ 'brightgreen',
+ )
+ expect(await versionColorForWordpressVersion('11.2')).to.equal(
+ 'brightgreen',
+ )
+ expect(await versionColorForWordpressVersion('3.2.0')).to.equal('yellow')
+ expect(await versionColorForWordpressVersion('3.2')).to.equal('yellow')
+ expect(await versionColorForWordpressVersion('4.7-beta.3')).to.equal(
+ 'yellow',
+ )
+ expect(await versionColorForWordpressVersion('cheese')).to.equal(
+ 'lightgrey',
+ )
+ })
+})
diff --git a/services/wordpress/wordpress-version-color.js b/services/wordpress/wordpress-version-color.js
index fd46f718987a2..7f342c6c94876 100644
--- a/services/wordpress/wordpress-version-color.js
+++ b/services/wordpress/wordpress-version-color.js
@@ -1,32 +1,32 @@
-'use strict'
+import Joi from 'joi'
+import semver from 'semver'
+import { getCachedResource } from '../../core/base-service/resource-cache.js'
+import { optionalDottedVersionNClausesWithOptionalSuffix } from '../validators.js'
-const { promisify } = require('util')
-const semver = require('semver')
-const { regularUpdate } = require('../../core/legacy/regular-update')
-
-// TODO: Incorporate this schema.
-// const schema = Joi.object()
-// .keys({
-// offers: Joi.array()
-// .items(
-// Joi.object()
-// .keys({
-// version: Joi.string()
-// .regex(/^\d+(\.\d+)?(\.\d+)?$/)
-// .required(),
-// })
-// .required()
-// )
-// .required(),
-// })
-// .required()
+const schema = Joi.object()
+ .keys({
+ offers: Joi.array()
+ .items(
+ Joi.object()
+ .keys({
+ version: optionalDottedVersionNClausesWithOptionalSuffix,
+ })
+ .required(),
+ )
+ .required(),
+ })
+ .required()
-function getOfferedVersions() {
- return promisify(regularUpdate)({
+async function getOfferedVersions() {
+ return getCachedResource({
url: 'https://api.wordpress.org/core/version-check/1.7/',
- intervalMillis: 24 * 3600 * 1000,
- json: true,
- scraper: json => json.offers.map(v => v.version),
+ scraper: json => {
+ const { error, value } = schema.validate(json, { allowUnknown: true })
+ if (error) {
+ throw Error(`WordPress version API response: ${error.message}`)
+ }
+ return value.offers.map(v => v.version)
+ },
})
}
@@ -61,8 +61,4 @@ async function versionColorForWordpressVersion(version) {
}
}
-module.exports = {
- toSemver,
- getOfferedVersions,
- versionColorForWordpressVersion,
-}
+export { toSemver, getOfferedVersions, versionColorForWordpressVersion }
diff --git a/services/wordpress/wordpress-version-color.spec.js b/services/wordpress/wordpress-version-color.spec.js
index f13412e5ec53f..60ba668b4d8b1 100644
--- a/services/wordpress/wordpress-version-color.spec.js
+++ b/services/wordpress/wordpress-version-color.spec.js
@@ -1,13 +1,8 @@
-'use strict'
+import { expect } from 'chai'
+import { toSemver } from './wordpress-version-color.js'
-const { expect } = require('chai')
-const {
- toSemver,
- versionColorForWordpressVersion,
-} = require('./wordpress-version-color')
-
-describe('toSemver() function', function() {
- it('coerces versions', function() {
+describe('toSemver() function', function () {
+ it('coerces versions', function () {
expect(toSemver('1.1.1')).to.equal('1.1.1')
expect(toSemver('4.2')).to.equal('4.2.0')
expect(toSemver('1.0.0-beta')).to.equal('1.0.0-beta')
@@ -15,24 +10,3 @@ describe('toSemver() function', function() {
expect(toSemver('foobar')).to.equal('foobar')
})
})
-
-describe('versionColorForWordpressVersion()', function() {
- it('generates correct colours for given versions', async function() {
- this.timeout(5e3)
-
- expect(await versionColorForWordpressVersion('11.2.0')).to.equal(
- 'brightgreen'
- )
- expect(await versionColorForWordpressVersion('11.2')).to.equal(
- 'brightgreen'
- )
- expect(await versionColorForWordpressVersion('3.2.0')).to.equal('yellow')
- expect(await versionColorForWordpressVersion('3.2')).to.equal('yellow')
- expect(await versionColorForWordpressVersion('4.7-beta.3')).to.equal(
- 'yellow'
- )
- expect(await versionColorForWordpressVersion('cheese')).to.equal(
- 'lightgrey'
- )
- })
-})
diff --git a/services/wordpress/wordpress-version.service.js b/services/wordpress/wordpress-version.service.js
index 3360fc40b678c..3fb9273bbfa58 100644
--- a/services/wordpress/wordpress-version.service.js
+++ b/services/wordpress/wordpress-version.service.js
@@ -1,8 +1,6 @@
-'use strict'
-
-const { addv } = require('../text-formatters')
-const { version: versionColor } = require('../color-formatters')
-const BaseWordpress = require('./wordpress-base')
+import { pathParams } from '../index.js'
+import { renderVersionBadge } from '../version.js'
+import { description, BaseWordpress } from './wordpress-base.js'
function VersionForExtensionType(extensionType) {
const { capt, exampleSlug } = {
@@ -17,50 +15,41 @@ function VersionForExtensionType(extensionType) {
}[extensionType]
return class WordpressVersion extends BaseWordpress {
- static get name() {
- return `Wordpress${capt}Version`
- }
+ static name = `Wordpress${capt}Version`
- static get category() {
- return 'version'
- }
+ static category = 'version'
- static get route() {
- return {
- base: `wordpress/${extensionType}/v`,
- pattern: ':slug',
- }
+ static route = {
+ base: `wordpress/${extensionType}/v`,
+ pattern: ':slug',
}
- static get examples() {
- return [
- {
- title: `WordPress ${capt} Version`,
- namedParams: { slug: exampleSlug },
- staticPreview: this.render({ version: 2.5 }),
+ static get openApi() {
+ const key = `/wordpress/${extensionType}/v/{slug}`
+ const route = {}
+ route[key] = {
+ get: {
+ summary: `WordPress ${capt} Version`,
+ description,
+ parameters: pathParams({
+ name: 'slug',
+ example: exampleSlug,
+ }),
},
- ]
- }
-
- static get defaultBadgeData() {
- return { label: extensionType }
- }
-
- static render({ version }) {
- return {
- message: addv(version),
- color: versionColor(version),
}
+ return route
}
+ static defaultBadgeData = { label: extensionType }
+
async handle({ slug }) {
const { version } = await this.fetch({
extensionType,
slug,
})
- return this.constructor.render({ version })
+ return renderVersionBadge({ version })
}
}
}
-module.exports = ['theme', 'plugin'].map(VersionForExtensionType)
+export default ['theme', 'plugin'].map(VersionForExtensionType)
diff --git a/services/wordpress/wordpress-version.tester.js b/services/wordpress/wordpress-version.tester.js
index 8668672523664..9abf4a330e7b1 100644
--- a/services/wordpress/wordpress-version.tester.js
+++ b/services/wordpress/wordpress-version.tester.js
@@ -1,37 +1,27 @@
-'use strict'
+import { ServiceTester } from '../tester.js'
+import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
-const { ServiceTester } = require('../tester')
-const { isVPlusDottedVersionAtLeastOne } = require('../test-validators')
-
-const t = (module.exports = new ServiceTester({
+export const t = new ServiceTester({
id: 'wordpress',
title: 'WordPress Version Tests',
-}))
+})
-t.create('Plugin Version')
- .get('/plugin/v/akismet.json')
- .expectBadge({
- label: 'plugin',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('Plugin Version').get('/plugin/v/akismet.json').expectBadge({
+ label: 'plugin',
+ message: isVPlusDottedVersionAtLeastOne,
+})
-t.create('Theme Version')
- .get('/theme/v/twentyseventeen.json')
- .expectBadge({
- label: 'theme',
- message: isVPlusDottedVersionAtLeastOne,
- })
+t.create('Theme Version').get('/theme/v/twentyseventeen.json').expectBadge({
+ label: 'theme',
+ message: isVPlusDottedVersionAtLeastOne,
+})
-t.create('Plugin Version | Not Found')
- .get('/plugin/v/100.json')
- .expectBadge({
- label: 'plugin',
- message: 'not found',
- })
+t.create('Plugin Version | Not Found').get('/plugin/v/100.json').expectBadge({
+ label: 'plugin',
+ message: 'not found',
+})
-t.create('Theme Version | Not Found')
- .get('/theme/v/100.json')
- .expectBadge({
- label: 'theme',
- message: 'not found',
- })
+t.create('Theme Version | Not Found').get('/theme/v/100.json').expectBadge({
+ label: 'theme',
+ message: 'not found',
+})
diff --git a/services/youtube/youtube-base.js b/services/youtube/youtube-base.js
new file mode 100644
index 0000000000000..efb7ea5680190
--- /dev/null
+++ b/services/youtube/youtube-base.js
@@ -0,0 +1,107 @@
+import Joi from 'joi'
+import { BaseJsonService, NotFound } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+
+const description = `
+Shields.io is committed to protecting the privacy of its users. We do not collect, store, or track any personal
+information or data returned by the YouTube API Services. Our service is designed to generate badges based on
+public YouTube data, and we do not retain any user-specific information:
+* Data Collection: we do not collect, store, or process any personal data from users. The information
+retrieved from the YouTube API is used solely to generate badges in real-time and is not stored or saved by us.
+* Cookies and Tracking: Shields.io does not use cookies or any other tracking technologies to collect or store user data.
+* Data Sharing: no information retrieved via the YouTube API is shared with third parties or used beyond generating the
+requested badges.
+* Contact Information: if you have any questions or concerns about our data practices, please contact us at team at shields.io.
+
+By using the YouTube badge, you are:
+* agreeing to be bound by the YouTube Terms of Service, which can be found here: [https://www.youtube.com/t/terms](https://www.youtube.com/t/terms)
+* acknowledging and accepting the Google Privacy Policy, which can be found here: [https://policies.google.com/privacy](https://policies.google.com/privacy)
+`
+
+const schema = Joi.object({
+ pageInfo: Joi.object({
+ totalResults: nonNegativeInteger,
+ resultsPerPage: nonNegativeInteger,
+ }).required(),
+ items: Joi.array().items(
+ Joi.object({
+ statistics: Joi.alternatives(
+ Joi.object({
+ viewCount: nonNegativeInteger,
+ likeCount: nonNegativeInteger,
+ commentCount: nonNegativeInteger,
+ }),
+ Joi.object({
+ viewCount: nonNegativeInteger,
+ subscriberCount: nonNegativeInteger,
+ }),
+ ),
+ }),
+ ),
+}).required()
+
+class YouTubeBase extends BaseJsonService {
+ static category = 'social'
+
+ static auth = {
+ passKey: 'youtube_api_key',
+ authorizedOrigins: ['https://www.googleapis.com'],
+ isRequired: true,
+ }
+
+ static defaultBadgeData = {
+ label: 'youtube',
+ color: 'red',
+ namedLogo: 'youtube',
+ }
+
+ static renderSingleStat({ statistics, statisticName, id }) {
+ return {
+ label: `${statisticName}s`,
+ message: metric(statistics[`${statisticName}Count`]),
+ style: 'social',
+ link: `https://www.youtube.com/${this.type}/${encodeURIComponent(id)}`,
+ }
+ }
+
+ async fetch({ id }) {
+ return this._requestJson(
+ this.authHelper.withQueryStringAuth(
+ { passKey: 'key' },
+ {
+ schema,
+ url: `https://www.googleapis.com/youtube/v3/${this.constructor.type}s`,
+ options: {
+ searchParams: { id, part: 'statistics' },
+ },
+ httpErrors: {
+ 400: `${this.constructor.type} not found`,
+ },
+ },
+ ),
+ )
+ }
+
+ async handle({ channelId, videoId }) {
+ const id = channelId || videoId
+ const json = await this.fetch({ id })
+ if (json.pageInfo.totalResults === 0) {
+ throw new NotFound({
+ prettyMessage: `${this.constructor.type} not found`,
+ })
+ }
+ const statistics = json.items[0].statistics
+ return this.constructor.render({ statistics, id })
+ }
+}
+
+class YouTubeVideoBase extends YouTubeBase {
+ static type = 'video'
+}
+
+class YouTubeChannelBase extends YouTubeBase {
+ static type = 'channel'
+}
+
+export { description, YouTubeVideoBase, YouTubeChannelBase }
diff --git a/services/youtube/youtube-channel-views.service.js b/services/youtube/youtube-channel-views.service.js
new file mode 100644
index 0000000000000..ae1681129492d
--- /dev/null
+++ b/services/youtube/youtube-channel-views.service.js
@@ -0,0 +1,26 @@
+import { pathParams } from '../index.js'
+import { description, YouTubeChannelBase } from './youtube-base.js'
+
+export default class YouTubeChannelViews extends YouTubeChannelBase {
+ static route = {
+ base: 'youtube/channel/views',
+ pattern: ':channelId',
+ }
+
+ static openApi = {
+ '/youtube/channel/views/{channelId}': {
+ get: {
+ summary: 'YouTube Channel Views',
+ description,
+ parameters: pathParams({
+ name: 'channelId',
+ example: 'UC8butISFwT-Wl7EV0hUK0BQ',
+ }),
+ },
+ },
+ }
+
+ static render({ statistics, id }) {
+ return super.renderSingleStat({ statistics, statisticName: 'view', id })
+ }
+}
diff --git a/services/youtube/youtube-channel-views.tester.js b/services/youtube/youtube-channel-views.tester.js
new file mode 100644
index 0000000000000..02d316ecd8273
--- /dev/null
+++ b/services/youtube/youtube-channel-views.tester.js
@@ -0,0 +1,24 @@
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import _noYouTubeToken from './youtube-channel-views.service.js'
+export const t = await createServiceTester()
+const noYouTubeToken = noToken(_noYouTubeToken)
+
+t.create('channel view count')
+ .skipWhen(noYouTubeToken)
+ .get('/UC8butISFwT-Wl7EV0hUK0BQ.json')
+ .expectBadge({
+ label: 'views',
+ message: isMetric,
+ color: 'red',
+ link: ['https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ'],
+ })
+
+t.create('channel not found')
+ .skipWhen(noYouTubeToken)
+ .get('/doesnotexist.json')
+ .expectBadge({
+ label: 'youtube',
+ message: 'channel not found',
+ })
diff --git a/services/youtube/youtube-comments.service.js b/services/youtube/youtube-comments.service.js
new file mode 100644
index 0000000000000..dc003c472a8fd
--- /dev/null
+++ b/services/youtube/youtube-comments.service.js
@@ -0,0 +1,26 @@
+import { pathParams } from '../index.js'
+import { description, YouTubeVideoBase } from './youtube-base.js'
+
+export default class YouTubeComments extends YouTubeVideoBase {
+ static route = {
+ base: 'youtube/comments',
+ pattern: ':videoId',
+ }
+
+ static openApi = {
+ '/youtube/comments/{videoId}': {
+ get: {
+ summary: 'YouTube Video Comments',
+ description,
+ parameters: pathParams({
+ name: 'videoId',
+ example: 'wGJHwc5ksMA',
+ }),
+ },
+ },
+ }
+
+ static render({ statistics, id }) {
+ return super.renderSingleStat({ statistics, statisticName: 'comment', id })
+ }
+}
diff --git a/services/youtube/youtube-comments.tester.js b/services/youtube/youtube-comments.tester.js
new file mode 100644
index 0000000000000..dded2be9dfb28
--- /dev/null
+++ b/services/youtube/youtube-comments.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import _noYouTubeToken from './youtube-comments.service.js'
+export const t = await createServiceTester()
+const noYouTubeToken = noToken(_noYouTubeToken)
+
+t.create('video comment count')
+ .skipWhen(noYouTubeToken)
+ .get('/wGJHwc5ksMA.json')
+ .expectBadge({
+ label: 'comments',
+ message: isMetric,
+ color: 'red',
+ link: ['https://www.youtube.com/video/wGJHwc5ksMA'],
+ })
+
+t.create('video not found')
+ .skipWhen(noYouTubeToken)
+ .get('/doesnotexist.json')
+ .expectBadge({
+ label: 'youtube',
+ message: 'video not found',
+ color: 'red',
+ })
diff --git a/services/youtube/youtube-likes.service.js b/services/youtube/youtube-likes.service.js
new file mode 100644
index 0000000000000..017dfa2af0aaa
--- /dev/null
+++ b/services/youtube/youtube-likes.service.js
@@ -0,0 +1,30 @@
+import { pathParams } from '../index.js'
+import { description, YouTubeVideoBase } from './youtube-base.js'
+
+export default class YouTubeLikes extends YouTubeVideoBase {
+ static route = {
+ base: 'youtube/likes',
+ pattern: ':videoId',
+ }
+
+ static openApi = {
+ '/youtube/likes/{videoId}': {
+ get: {
+ summary: 'YouTube Video Likes',
+ description,
+ parameters: pathParams({
+ name: 'videoId',
+ example: 'abBdk8bSPKU',
+ }),
+ },
+ },
+ }
+
+ static render({ statistics, id }) {
+ return super.renderSingleStat({
+ statistics,
+ statisticName: 'like',
+ id,
+ })
+ }
+}
diff --git a/services/youtube/youtube-likes.tester.js b/services/youtube/youtube-likes.tester.js
new file mode 100644
index 0000000000000..366690aa03e16
--- /dev/null
+++ b/services/youtube/youtube-likes.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import _noYouTubeToken from './youtube-likes.service.js'
+export const t = await createServiceTester()
+const noYouTubeToken = noToken(_noYouTubeToken)
+
+t.create('video like count')
+ .skipWhen(noYouTubeToken)
+ .get('/pU9Q6oiQNd0.json')
+ .expectBadge({
+ label: 'likes',
+ message: isMetric,
+ color: 'red',
+ link: ['https://www.youtube.com/video/pU9Q6oiQNd0'],
+ })
+
+t.create('video not found')
+ .skipWhen(noYouTubeToken)
+ .get('/doesnotexist.json')
+ .expectBadge({
+ label: 'youtube',
+ message: 'video not found',
+ color: 'red',
+ })
diff --git a/services/youtube/youtube-subscribers.service.js b/services/youtube/youtube-subscribers.service.js
new file mode 100644
index 0000000000000..ffc6f319404c4
--- /dev/null
+++ b/services/youtube/youtube-subscribers.service.js
@@ -0,0 +1,30 @@
+import { pathParams } from '../index.js'
+import { description, YouTubeChannelBase } from './youtube-base.js'
+
+export default class YouTubeSubscribes extends YouTubeChannelBase {
+ static route = {
+ base: 'youtube/channel/subscribers',
+ pattern: ':channelId',
+ }
+
+ static openApi = {
+ '/youtube/channel/subscribers/{channelId}': {
+ get: {
+ summary: 'YouTube Channel Subscribers',
+ description,
+ parameters: pathParams({
+ name: 'channelId',
+ example: 'UC8butISFwT-Wl7EV0hUK0BQ',
+ }),
+ },
+ },
+ }
+
+ static render({ statistics, id }) {
+ return super.renderSingleStat({
+ statistics,
+ statisticName: 'subscriber',
+ id,
+ })
+ }
+}
diff --git a/services/youtube/youtube-subscribers.tester.js b/services/youtube/youtube-subscribers.tester.js
new file mode 100644
index 0000000000000..2a35d9daefb34
--- /dev/null
+++ b/services/youtube/youtube-subscribers.tester.js
@@ -0,0 +1,24 @@
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import _noYouTubeToken from './youtube-subscribers.service.js'
+export const t = await createServiceTester()
+const noYouTubeToken = noToken(_noYouTubeToken)
+
+t.create('subscriber count')
+ .skipWhen(noYouTubeToken)
+ .get('/UC8butISFwT-Wl7EV0hUK0BQ.json')
+ .expectBadge({
+ label: 'subscribers',
+ message: isMetric,
+ color: 'red',
+ link: ['https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ'],
+ })
+
+t.create('channel not found')
+ .skipWhen(noYouTubeToken)
+ .get('/doesnotexist.json')
+ .expectBadge({
+ label: 'youtube',
+ message: 'channel not found',
+ })
diff --git a/services/youtube/youtube-views.service.js b/services/youtube/youtube-views.service.js
new file mode 100644
index 0000000000000..a4728ca90e2aa
--- /dev/null
+++ b/services/youtube/youtube-views.service.js
@@ -0,0 +1,26 @@
+import { pathParams } from '../index.js'
+import { description, YouTubeVideoBase } from './youtube-base.js'
+
+export default class YouTubeViews extends YouTubeVideoBase {
+ static route = {
+ base: 'youtube/views',
+ pattern: ':videoId',
+ }
+
+ static openApi = {
+ '/youtube/views/{videoId}': {
+ get: {
+ summary: 'YouTube Video Views',
+ description,
+ parameters: pathParams({
+ name: 'videoId',
+ example: 'abBdk8bSPKU',
+ }),
+ },
+ },
+ }
+
+ static render({ statistics, id }) {
+ return super.renderSingleStat({ statistics, statisticName: 'view', id })
+ }
+}
diff --git a/services/youtube/youtube-views.tester.js b/services/youtube/youtube-views.tester.js
new file mode 100644
index 0000000000000..f825e71fb46f8
--- /dev/null
+++ b/services/youtube/youtube-views.tester.js
@@ -0,0 +1,25 @@
+import { createServiceTester } from '../tester.js'
+import { noToken } from '../test-helpers.js'
+import { isMetric } from '../test-validators.js'
+import _noYouTubeToken from './youtube-views.service.js'
+export const t = await createServiceTester()
+const noYouTubeToken = noToken(_noYouTubeToken)
+
+t.create('video view count')
+ .skipWhen(noYouTubeToken)
+ .get('/abBdk8bSPKU.json')
+ .expectBadge({
+ label: 'views',
+ message: isMetric,
+ color: 'red',
+ link: ['https://www.youtube.com/video/abBdk8bSPKU'],
+ })
+
+t.create('video not found')
+ .skipWhen(noYouTubeToken)
+ .get('/doesnotexist.json')
+ .expectBadge({
+ label: 'youtube',
+ message: 'video not found',
+ color: 'red',
+ })
diff --git a/spec/SPECIFICATION.md b/spec/SPECIFICATION.md
index de3a90668c197..9659080b0e3f8 100644
--- a/spec/SPECIFICATION.md
+++ b/spec/SPECIFICATION.md
@@ -8,19 +8,19 @@ This document specifies the visual design of Shields badges.
- **semantic**: the purpose of the information provided by a badge should be self-evident
- **non-promotional**: badges should refrain from advertising a service but instead provide value through the badge's information instead
- **concise**: one descriptive word on the left (the key), one piece of data on the right (the value)
-- **hyperlinked**: badges can link to a third-party website providing more information, either related to the metadata provided by the badge or about the project the badge was used for (e.g. an open source library)
+- **hyperlinked**: badges with style 'social' can link to a third-party website providing more information, either related to the metadata provided by the badge or about the project the badge was used for (e.g. an open source library)
### Example
#### Bad
-
+
The key is shamelessly promoting the service provider instead of giving context to the value. If service providers want to promote themselves, they can simply encourage people to link back to them and let folks who are curious click on the button for more information.
#### Good
-
+
The key clearly explains what the value stands for (the version of the software provided). The platform or service hosting the version of the software is only relevant to people who decide to click on an eventual link added to the badge itself but the value stands on its own with the metadata value it provides to viewers.
@@ -30,4 +30,4 @@ The design of our badges has been carefully considered to provide sufficient pad

-When it comes to color choices, the focus is on clear contrast between the text and the background color on both sides of the badge (key and value). The two sides are also contrasted with each other with the key side always retaining a dark grey color for consistency, and the value side taking on whichever background color better conveys the meaning of the data provided (e.g. green for a successful build, red for a failed build).
+When it comes to color choices, the focus is on clear contrast between the text and the background color on both sides of the badge (key and value). The two sides are also contrasted with each other with the key side generally retaining a dark grey color for consistency, and the value side taking on whichever background color better conveys the meaning of the data provided (e.g. green for a successful build, red for a failed build). The preferred text color is white, but black may also be chosen to ensure legibility when a light background is used for a given badge side.
diff --git a/spec/motivation.md b/spec/motivation.md
index 03c95de6b6abc..4c42f53cd17c7 100644
--- a/spec/motivation.md
+++ b/spec/motivation.md
@@ -29,4 +29,4 @@ As you can see below, without increasing the footprint of these badges, I've tri

-This badge design corresponds to an old and now deprecated version which has since been replaced by beautiful and scalable SVG versions that can be found on [shields.io](http://shields.io)
+This badge design corresponds to an old and now deprecated version which has since been replaced by beautiful and scalable SVG versions that can be found on [shields.io](https://shields.io)
diff --git a/spec/users.md b/spec/users.md
deleted file mode 100644
index b7d5391cfb16e..0000000000000
--- a/spec/users.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Services using the Shields standard
-
-- [Badger](https://github.com/badges/badgerbadgerbadger)
-- [badges2svg](https://github.com/bfontaine/badges2svg)
-- [CII Best Practices](https://bestpractices.coreinfrastructure.org/)
-- [Codacy](https://www.codacy.com)
-- [Code Climate](https://codeclimate.com/changelog/510d4fde56b102523a0004bf)
-- [Coveralls](https://coveralls.io/)
-- [docs.rs](https://docs.rs/)
-- [Forkability](http://basicallydan.github.io/forkability/)
-- [Gemnasium](http://support.gemnasium.com/forums/236528-general/suggestions/5518400-use-svg-for-badges-so-they-still-look-sharp-on-r)
-- [GoDoc](https://godoc.org/)
-- [PHPPackages](https://phppackages.org)
-- [Read the Docs](https://readthedocs.org/)
-- [reposs](https://github.com/rexfinn/reposs)
-- [ruby-gem-downloads-badge](https://github.com/bogdanRada/ruby-gem-downloads-badge/)
-- [Scrutinizer](https://scrutinizer-ci.com/)
-- [Semaphore](https://semaphoreci.com)
-- [Travis CI](https://github.com/travis-ci/travis-ci/issues/630#issuecomment-38054967)
-- [Version Badge](http://badge.fury.io/)
-- [VersionEye](https://www.versioneye.com/)
diff --git a/tsconfig.json b/tsconfig.json
deleted file mode 100644
index 1063d8f664a2e..0000000000000
--- a/tsconfig.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "include": ["frontend/**/*"],
- "exclude": [],
- "compilerOptions": {
- "target": "esnext",
- "module": "commonjs",
- "declaration": true,
- "outDir": "unused_ts_output",
- "strict": true,
- "allowSyntheticDefaultImports": true,
- "esModuleInterop": true,
- "jsx": "react",
- "typeRoots": ["node_modules/@types", "frontend/types"]
- }
-}
diff --git a/vendor/http-deceiver/README.md b/vendor/http-deceiver/README.md
new file mode 100644
index 0000000000000..e26298ad9c900
--- /dev/null
+++ b/vendor/http-deceiver/README.md
@@ -0,0 +1,17 @@
+# vendor/http-deceiver
+
+This is a workaround to bypass the blocker for https://github.com/badges/shields/issues/11071.
+
+Related PR: https://github.com/badges/shields/pull/11279.
+
+## LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2015.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/http-deceiver/index.js b/vendor/http-deceiver/index.js
new file mode 100644
index 0000000000000..fbb9dd8107d31
--- /dev/null
+++ b/vendor/http-deceiver/index.js
@@ -0,0 +1,288 @@
+/* eslint-disable */
+/**
+ * The related issue:
+ * https://github.com/badges/shields/issues/11071
+ *
+ * The code below is a modified version of the original http-deceiver library.
+ * It has been adapted to work with Node.js versions that have deprecated
+ * the original implementation.
+ *
+ * The original library can be found at:
+ * https://github.com/spdy-http2/http-deceiver (MIT License)
+ *
+ * This version is specifically tailored to handle the deprecation of
+ * the `http_parser` module in Node.js v12 and later.
+ * https://github.com/beenotung/http-deceiver/blob/node-v12-deprecation-fix/lib/deceiver.js (MIT License)
+ *
+ * It maintains compatibility with older Node.js versions while providing
+ * a consistent interface for HTTP request and response parsing.
+ */
+var assert = require('assert')
+
+var Buffer = require('buffer').Buffer
+
+// Node.js version
+var mode = /^v0\.8\./.test(process.version)
+ ? 'rusty'
+ : /^v0\.(9|10)\./.test(process.version)
+ ? 'old'
+ : /^v0\.12\./.test(process.version)
+ ? 'normal'
+ : 'modern'
+
+var HTTPParser
+
+var methods
+var reverseMethods
+
+// var kOnHeaders
+var kOnHeadersComplete
+var kOnMessageComplete
+var kOnBody
+if (mode === 'normal' || mode === 'modern') {
+ HTTPParser = require('_http_common').HTTPParser
+ methods = require('_http_common').methods
+
+ // <= v11
+ if (!HTTPParser) {
+ HTTPParser = process.binding('http_parser').HTTPParser
+ methods = process.binding('http_parser').methods
+ }
+
+ // <= v5
+ if (!methods) {
+ methods = HTTPParser.methods
+ }
+
+ reverseMethods = {}
+
+ methods.forEach(function (method, index) {
+ reverseMethods[method] = index
+ })
+
+ // kOnHeaders = HTTPParser.kOnHeaders | 0
+ kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0
+ kOnMessageComplete = HTTPParser.kOnMessageComplete | 0
+ kOnBody = HTTPParser.kOnBody | 0
+} else {
+ // kOnHeaders = 'onHeaders'
+ kOnHeadersComplete = 'onHeadersComplete'
+ kOnMessageComplete = 'onMessageComplete'
+ kOnBody = 'onBody'
+}
+
+function Deceiver(socket, options) {
+ this.socket = socket
+ this.options = options || {}
+ this.isClient = this.options.isClient
+}
+module.exports = Deceiver
+
+Deceiver.create = function create(stream, options) {
+ return new Deceiver(stream, options)
+}
+
+Deceiver.prototype._toHeaderList = function _toHeaderList(object) {
+ var out = []
+ var keys = Object.keys(object)
+
+ for (var i = 0; i < keys.length; i++) {
+ out.push(keys[i], object[keys[i]])
+ }
+
+ return out
+}
+
+Deceiver.prototype._isUpgrade = function _isUpgrade(request) {
+ return (
+ request.method === 'CONNECT' ||
+ request.headers.upgrade ||
+ (request.headers.connection &&
+ /(^|\W)upgrade(\W|$)/i.test(request.headers.connection))
+ )
+}
+
+// TODO(indutny): support CONNECT
+if (mode === 'modern') {
+ /*
+ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
+ url, statusCode, statusMessage, upgrade,
+ shouldKeepAlive) {
+ */
+ Deceiver.prototype.emitRequest = function emitRequest(request) {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ parser.execute = null
+
+ var self = this
+ var method = reverseMethods[request.method]
+ parser.execute = function execute() {
+ self._skipExecute(this)
+ this[kOnHeadersComplete](
+ 1,
+ 1,
+ self._toHeaderList(request.headers),
+ method,
+ request.path,
+ 0,
+ '',
+ self._isUpgrade(request),
+ true,
+ )
+ return 0
+ }
+
+ this._emitEmpty()
+ }
+
+ Deceiver.prototype.emitResponse = function emitResponse(response) {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ parser.execute = null
+
+ var self = this
+ parser.execute = function execute() {
+ self._skipExecute(this)
+ this[kOnHeadersComplete](
+ 1,
+ 1,
+ self._toHeaderList(response.headers),
+ response.path,
+ response.code,
+ response.status,
+ response.reason || '',
+ self._isUpgrade(response),
+ true,
+ )
+ return 0
+ }
+
+ this._emitEmpty()
+ }
+} else {
+ /*
+ `function parserOnHeadersComplete(info) {`
+
+ info = { .versionMajor, .versionMinor, .url, .headers, .method,
+ .statusCode, .statusMessage, .upgrade, .shouldKeepAlive }
+ */
+ Deceiver.prototype.emitRequest = function emitRequest(request) {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ var method = request.method
+ if (reverseMethods) {
+ method = reverseMethods[method]
+ }
+
+ var info = {
+ versionMajor: 1,
+ versionMinor: 1,
+ url: request.path,
+ headers: this._toHeaderList(request.headers),
+ method: method,
+ statusCode: 0,
+ statusMessage: '',
+ upgrade: this._isUpgrade(request),
+ shouldKeepAlive: true,
+ }
+
+ var self = this
+ parser.execute = function execute() {
+ self._skipExecute(this)
+ this[kOnHeadersComplete](info)
+ return 0
+ }
+
+ this._emitEmpty()
+ }
+
+ Deceiver.prototype.emitResponse = function emitResponse(response) {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ var info = {
+ versionMajor: 1,
+ versionMinor: 1,
+ url: response.path,
+ headers: this._toHeaderList(response.headers),
+ method: false,
+ statusCode: response.status,
+ statusMessage: response.reason || '',
+ upgrade: this._isUpgrade(response),
+ shouldKeepAlive: true,
+ }
+
+ var self = this
+ parser.execute = function execute() {
+ self._skipExecute(this)
+ this[kOnHeadersComplete](info)
+ return 0
+ }
+
+ this._emitEmpty()
+ }
+}
+
+Deceiver.prototype._skipExecute = function _skipExecute(parser) {
+ var self = this
+ var oldExecute = parser.constructor.prototype.execute
+ var oldFinish = parser.constructor.prototype.finish
+
+ parser.execute = null
+ parser.finish = null
+
+ parser.execute = function execute(buffer, start, len) {
+ // Parser reuse
+ if (this.socket !== self.socket) {
+ this.execute = oldExecute
+ this.finish = oldFinish
+ return this.execute(buffer, start, len)
+ }
+
+ if (start !== undefined) {
+ buffer = buffer.slice(start, start + len)
+ }
+ self.emitBody(buffer)
+ return len
+ }
+
+ parser.finish = function finish() {
+ // Parser reuse
+ if (this.socket !== self.socket) {
+ this.execute = oldExecute
+ this.finish = oldFinish
+ return this.finish()
+ }
+
+ this.execute = oldExecute
+ this.finish = oldFinish
+ self.emitMessageComplete()
+ }
+}
+
+Deceiver.prototype.emitBody = function emitBody(buffer) {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ parser[kOnBody](buffer, 0, buffer.length)
+}
+
+Deceiver.prototype._emitEmpty = function _emitEmpty() {
+ // Emit data to force out handling of UPGRADE
+ var empty = new Buffer(0)
+ if (this.socket.ondata) {
+ this.socket.ondata(empty, 0, 0)
+ } else {
+ this.socket.emit('data', empty)
+ }
+}
+
+Deceiver.prototype.emitMessageComplete = function emitMessageComplete() {
+ var parser = this.socket.parser
+ assert(parser, 'No parser present')
+
+ parser[kOnMessageComplete]()
+}
diff --git a/vendor/http-deceiver/package.json b/vendor/http-deceiver/package.json
new file mode 100644
index 0000000000000..0967ef424bce6
--- /dev/null
+++ b/vendor/http-deceiver/package.json
@@ -0,0 +1 @@
+{}