diff --git a/assets/icons/crown.svg b/assets/icons/crown.svg new file mode 100644 index 0000000..280be59 --- /dev/null +++ b/assets/icons/crown.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/sponsors/descope.png b/assets/sponsors/descope.png new file mode 100644 index 0000000..dd1e74d Binary files /dev/null and b/assets/sponsors/descope.png differ diff --git a/assets/sponsors/descope_white.png b/assets/sponsors/descope_white.png new file mode 100644 index 0000000..449e1a0 Binary files /dev/null and b/assets/sponsors/descope_white.png differ diff --git a/assets/sponsors/principal.svg b/assets/sponsors/principal.svg new file mode 100644 index 0000000..57b2c0a --- /dev/null +++ b/assets/sponsors/principal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/sponsors/route4me_icon_55e4b23adce0b1e9f721fc797800e1839c230f79.png b/assets/sponsors/route4me_icon_55e4b23adce0b1e9f721fc797800e1839c230f79.png deleted file mode 100644 index 5142814..0000000 Binary files a/assets/sponsors/route4me_icon_55e4b23adce0b1e9f721fc797800e1839c230f79.png and /dev/null differ diff --git a/assets/sponsors/route4me_white.png b/assets/sponsors/route4me_white.png new file mode 100644 index 0000000..74f2af3 Binary files /dev/null and b/assets/sponsors/route4me_white.png differ diff --git a/assets/sponsors/stytch.png b/assets/sponsors/stytch.png new file mode 100644 index 0000000..264d97e Binary files /dev/null and b/assets/sponsors/stytch.png differ diff --git a/assets/sponsors/stytch_white.png b/assets/sponsors/stytch_white.png new file mode 100644 index 0000000..f5a0bfb Binary files /dev/null and b/assets/sponsors/stytch_white.png differ diff --git a/data/sponsors.json b/data/sponsors.json index d67075f..a69e553 100644 --- a/data/sponsors.json +++ b/data/sponsors.json @@ -2,9 +2,16 @@ "totalAmountDonatedThreshold": 200, "monthlyContributionThreshold": 15, "disappearCredit": 20, - "scoreTierPriceFactor": 0.70, + "scoreTierPriceFactor": 0.80, "scoreTotalAmountFactor": 0.2, "tiers": { + "backer" : { + "price": 20, + "benefits": { + "showAtSponsorList": true + }, + "period": 2 + }, "bronze": { "price": 49, "benefits": { @@ -15,21 +22,34 @@ "price": 99, "benefits": { "showAtSponsorList": true, - "showAtPages": true + "github": true, + "showAtPages": false } }, "gold" : { "price": 299, "benefits": { "showAtSponsorList": true, - "showAtPages": true + "github": true, + "showAtPages": true, + "links": 3, + "video": true, + "readme": true, + "crown": true, + "maxReadmeDescLength": 100 } }, "platinum" : { "price": 599, "benefits": { "showAtSponsorList": true, - "showAtPages": true + "github": true, + "showAtPages": true, + "links": 4, + "video": true, + "readme": true, + "crown": true, + "maxReadmeDescLength": 150 } } }, @@ -51,10 +71,13 @@ "hide": true }, "route4me": { + "tier": "gold", "image": "/assets/sponsors/route4me.png", + "image_dark": "/assets/sponsors/route4me_white.png", "website": "https://route4me.com/", "displayName": "Route4Me", "alt": "Route Planner and Route Optimizer", + "description": "Best Route Planning And Route Optimization Software", "isActive": true, "totalAmountDonated": 300, "createdAt": "2024-07-12 00:00", @@ -67,6 +90,33 @@ }, "slotozilla-deutschland" : { "autoUTMLinks": false + }, + "stytch": { + "tier": "gold", + "description": "API-first authentication, authorization, and fraud prevention", + "image": "/assets/sponsors/stytch.png", + "image_dark": "/assets/sponsors/stytch_white.png", + "targetLink": "https://stytch.com/?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_content=website-link&utm_campaign=axios-http", + "links": { + "Website": "https://stytch.com/?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_content=website-link&utm_campaign=axios-http", + "Documentation": "https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_content=docs-link&utm_campaign=axios-http", + "Node.js": "https://github.com/stytchauth/stytch-node?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_content=node-sdk&utm_campaign=axios-http" + } + }, + "descope": { + "tier": "gold", + "image": "/assets/sponsors/descope.png", + "image_dark": "/assets/sponsors/descope_white.png", + "targetLink": "https://www.descope.com/?utm_source=axios&utm_medium=referral&utm_campaign=axios-oss-sponsorship", + "links": { + "Website": "https://www.descope.com/?utm_source=axios&utm_medium=referral&utm_campaign=axios-oss-sponsorship", + "Documentation": "https://docs.descope.com/?utm_source=axios&utm_medium=referral&utm_campaign=axios-oss-sponsorship", + "Community": "https://www.descope.com/community?utm_source=axios&utm_medium=referral&utm_campaign=axios-oss-sponsorship" + } + }, + "principal": { + "tier": "gold", + "image": "/assets/sponsors/principal.svg" } } } diff --git a/en.lang.js b/en.lang.js index 0078f25..aea2a46 100644 --- a/en.lang.js +++ b/en.lang.js @@ -15,7 +15,9 @@ module.exports = { Axios provides a simple to use library in a small package with a very extensible interface.`, sponsors: `

Thank you for considering supporting our project.

-

You will be automatically added to this list within 24 hours if the tier you select provides this benefit.

` +

You will be automatically added to this list within 24 hours if the tier you select provides this benefit.

+
Read more...
+ ` }, // `t` stands fot `translation`. This will contain translations of single words or phrases t: { @@ -38,7 +40,8 @@ module.exports = { "Axios Project Copy Right Footer": undefined, "License Label Footer": undefined, "Sponsors": undefined, - "Become a sponsor": undefined + "Become a sponsor": undefined, + "Gold Sponsors": undefined }, sidebar: [ { @@ -127,6 +130,11 @@ module.exports = { type: "heading", text: "Contributors", }, + { + type: "link", + href: "/docs/sponsor", + text: "Sponsoring Axios", + }, { type: "link", href: "https://github.com/axios/axios/blob/master/CODE_OF_CONDUCT.md", diff --git a/inert.config.js b/inert.config.js index 06495cd..f656ece 100644 --- a/inert.config.js +++ b/inert.config.js @@ -328,7 +328,7 @@ module.exports = { }, ...langs.map( (lang) => ({ - folder: `posts/${lang.postsDir || lang.prefix.slice(1, -1)}`, + folder: `posts/${lang.postsDir || lang.prefix.slice(1, -1)}`, build: { traverseLevel: "recursive", filePipeline: [ diff --git a/package-lock.json b/package-lock.json index 4f0f557..164c84e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,20 +8,50 @@ "name": "axios-docs", "version": "0.4.0", "license": "MIT", + "dependencies": { + "graphql": "^16.9.0", + "graphql-request": "^7.1.0" + }, "devDependencies": { "axios": "^1.3.2", "cheerio": "^1.0.0-rc.12", + "handlebars": "^4.7.8", "html-escaper": "^2.0.2", "http-server": "^14.1.0", "inert-ssg": "^2.0.0-alpha.15", + "joi": "^17.13.3", "mime-types": "^2.1.35", - "sharp": "^0.31.3" + "sharp": "^0.31.3", + "showdown": "^2.1.0" }, "engines": { "node": "16", "npm": "8" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "peerDependencies": { + "graphql": "^0.8.0 || ^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/@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 + }, + "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, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@jsbits/get-package-version": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@jsbits/get-package-version/-/get-package-version-1.0.3.tgz", @@ -31,12 +61,120 @@ "node": ">=4.2" } }, + "node_modules/@molt/command": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@molt/command/-/command-0.9.0.tgz", + "integrity": "sha512-1JI8dAlpqlZoXyKWVQggX7geFNPxBpocHIXQCsnxDjKy+3WX4SGyZVJXuLlqRRrX7FmQCuuMAfx642ovXmPA9g==", + "dependencies": { + "@molt/types": "0.2.0", + "alge": "0.8.1", + "chalk": "^5.3.0", + "lodash.camelcase": "^4.3.0", + "lodash.snakecase": "^4.1.1", + "readline-sync": "^1.4.10", + "string-length": "^6.0.0", + "strip-ansi": "^7.1.0", + "ts-toolbelt": "^9.6.0", + "type-fest": "^4.3.1", + "zod": "^3.22.2" + } + }, + "node_modules/@molt/command/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==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@molt/command/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@molt/command/node_modules/string-length": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz", + "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==", + "dependencies": { + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@molt/command/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==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@molt/types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@molt/types/-/types-0.2.0.tgz", + "integrity": "sha512-p6ChnEZDGjg9PYPec9BK6Yp5/DdSrYQvXTBAtgrnqX6N36cZy37ql1c8Tc5LclfIYBNG7EZp8NBcRTYJwyi84g==", + "dependencies": { + "ts-toolbelt": "^9.6.0" + } + }, + "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, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "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 + }, + "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 + }, "node_modules/@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, + "node_modules/alge": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/alge/-/alge-0.8.1.tgz", + "integrity": "sha512-kiV9nTt+XIauAXsowVygDxMZLplZxDWt0W8plE/nB32/V2ziM/P/TxDbSVK7FYIUt2Xo16h3/htDh199LNPCKQ==", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "remeda": "^1.0.0", + "ts-toolbelt": "^9.6.0", + "zod": "^3.17.3" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -514,6 +652,15 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -998,6 +1145,71 @@ "node": ">= 6" } }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.1.0.tgz", + "integrity": "sha512-Ouu/lYVFhARS1aXeZoVJWnGT6grFJXTLwXJuK4mUGGRo0EUk1JkyYp43mdGmRgUVezpRm6V5Sq3t8jBDQcajng==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "@molt/command": "^0.9.0", + "zod": "^3.23.8" + }, + "bin": { + "graffle": "build/cli/generate.js" + }, + "peerDependencies": { + "@dprint/formatter": "^0.3.0", + "@dprint/typescript": "^0.91.1", + "dprint": "^0.46.2", + "graphql": "14 - 16" + }, + "peerDependenciesMeta": { + "@dprint/formatter": { + "optional": true + }, + "@dprint/typescript": { + "optional": true + }, + "dprint": { + "optional": true + } + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1569,6 +1781,19 @@ "node": ">=4" } }, + "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, + "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" + } + }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1597,12 +1822,22 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.deburr": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=", "dev": true }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==" + }, "node_modules/lodash.kebabcase": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz", @@ -1613,6 +1848,11 @@ "lodash.words": "^4.0.0" } }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "node_modules/lodash.words": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.words/-/lodash.words-4.2.0.tgz", @@ -1821,6 +2061,12 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "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/node-abi": { "version": "3.33.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", @@ -2176,6 +2422,19 @@ "node": ">=8.10.0" } }, + "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==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/remeda": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-1.61.0.tgz", + "integrity": "sha512-caKfSz9rDeSKBQQnlJnVW3mbVdFgxgGWQKq1XlFokqjf+hQD5gxutLGTTY2A/x24UxVyJe9gH5fAkFI63ULw4A==" + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -2307,6 +2566,22 @@ "node": ">=8" } }, + "node_modules/showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "dev": true, + "dependencies": { + "commander": "^9.0.0" + }, + "bin": { + "showdown": "bin/showdown.js" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -2381,6 +2656,15 @@ "is-arrayish": "^0.3.1" } }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2535,6 +2819,11 @@ "node": ">=8.0" } }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2547,12 +2836,36 @@ "node": "*" } }, + "node_modules/type-fest": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.24.0.tgz", + "integrity": "sha512-spAaHzc6qre0TlZQQ2aA/nGMe+2Z/wyGk5Z+Ru2VUfdNwT6kWO6TjevOlpebsATEG1EIQ2sOiDszud3lO5mt/Q==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/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 }, + "node_modules/uglify-js": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", + "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unidecode": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz", @@ -2691,21 +3004,136 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "requires": {} + }, + "@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 + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@jsbits/get-package-version": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@jsbits/get-package-version/-/get-package-version-1.0.3.tgz", "integrity": "sha512-IJy1jRL01x7p6UEpgKa1lVLstMUx8EiIR8pPoS5sBfsHEoeLkzYiNpAfxPx8zLDUJyS1yBbChJjcWdPqyH285w==", "dev": true }, + "@molt/command": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@molt/command/-/command-0.9.0.tgz", + "integrity": "sha512-1JI8dAlpqlZoXyKWVQggX7geFNPxBpocHIXQCsnxDjKy+3WX4SGyZVJXuLlqRRrX7FmQCuuMAfx642ovXmPA9g==", + "requires": { + "@molt/types": "0.2.0", + "alge": "0.8.1", + "chalk": "^5.3.0", + "lodash.camelcase": "^4.3.0", + "lodash.snakecase": "^4.1.1", + "readline-sync": "^1.4.10", + "string-length": "^6.0.0", + "strip-ansi": "^7.1.0", + "ts-toolbelt": "^9.6.0", + "type-fest": "^4.3.1", + "zod": "^3.22.2" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + }, + "string-length": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz", + "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==", + "requires": { + "strip-ansi": "^7.1.0" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "@molt/types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@molt/types/-/types-0.2.0.tgz", + "integrity": "sha512-p6ChnEZDGjg9PYPec9BK6Yp5/DdSrYQvXTBAtgrnqX6N36cZy37ql1c8Tc5LclfIYBNG7EZp8NBcRTYJwyi84g==", + "requires": { + "ts-toolbelt": "^9.6.0" + } + }, + "@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": { + "@hapi/hoek": "^9.0.0" + } + }, + "@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 + }, + "@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 + }, "@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, + "alge": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/alge/-/alge-0.8.1.tgz", + "integrity": "sha512-kiV9nTt+XIauAXsowVygDxMZLplZxDWt0W8plE/nB32/V2ziM/P/TxDbSVK7FYIUt2Xo16h3/htDh199LNPCKQ==", + "requires": { + "lodash.ismatch": "^4.4.0", + "remeda": "^1.0.0", + "ts-toolbelt": "^9.6.0", + "zod": "^3.17.3" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -3073,6 +3501,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3431,6 +3865,42 @@ "is-glob": "^4.0.1" } }, + "graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==" + }, + "graphql-request": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.1.0.tgz", + "integrity": "sha512-Ouu/lYVFhARS1aXeZoVJWnGT6grFJXTLwXJuK4mUGGRo0EUk1JkyYp43mdGmRgUVezpRm6V5Sq3t8jBDQcajng==", + "requires": { + "@graphql-typed-document-node/core": "^3.2.0", + "@molt/command": "^0.9.0", + "zod": "^3.23.8" + } + }, + "handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + } + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3865,6 +4335,19 @@ } } }, + "joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "requires": { + "@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" + } + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -3890,12 +4373,22 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "lodash.deburr": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", "integrity": "sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=", "dev": true }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==" + }, "lodash.kebabcase": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz", @@ -3906,6 +4399,11 @@ "lodash.words": "^4.0.0" } }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "lodash.words": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.words/-/lodash.words-4.2.0.tgz", @@ -4068,6 +4566,12 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "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-abi": { "version": "3.33.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", @@ -4346,6 +4850,16 @@ "picomatch": "^2.2.1" } }, + "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==" + }, + "remeda": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-1.61.0.tgz", + "integrity": "sha512-caKfSz9rDeSKBQQnlJnVW3mbVdFgxgGWQKq1XlFokqjf+hQD5gxutLGTTY2A/x24UxVyJe9gH5fAkFI63ULw4A==" + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -4435,6 +4949,15 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "dev": true, + "requires": { + "commander": "^9.0.0" + } + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4478,6 +5001,12 @@ "is-arrayish": "^0.3.1" } }, + "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 + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4604,6 +5133,11 @@ "is-number": "^7.0.0" } }, + "ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4613,12 +5147,24 @@ "safe-buffer": "^5.0.1" } }, + "type-fest": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.24.0.tgz", + "integrity": "sha512-spAaHzc6qre0TlZQQ2aA/nGMe+2Z/wyGk5Z+Ru2VUfdNwT6kWO6TjevOlpebsATEG1EIQ2sOiDszud3lO5mt/Q==" + }, "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 }, + "uglify-js": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", + "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "dev": true, + "optional": true + }, "unidecode": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz", @@ -4729,6 +5275,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" } } } diff --git a/package.json b/package.json index aa499ee..08d1ebb 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,13 @@ "devDependencies": { "axios": "^1.3.2", "cheerio": "^1.0.0-rc.12", + "handlebars": "^4.7.8", "html-escaper": "^2.0.2", "http-server": "^14.1.0", "inert-ssg": "^2.0.0-alpha.15", + "joi": "^17.13.3", "mime-types": "^2.1.35", - "sharp": "^0.31.3" + "sharp": "^0.31.3", + "showdown": "^2.1.0" } } diff --git a/posts/en/api_intro.md b/posts/en/api_intro.md index 2141970..e0c4e7b 100644 --- a/posts/en/api_intro.md +++ b/posts/en/api_intro.md @@ -58,5 +58,4 @@ For convenience aliases have been provided for all supported request methods. ##### axios.putForm(url[, data[, config]]) ##### axios.patchForm(url[, data[, config]]) -###### NOTE -When using the alias methods `url`, `method`, and `data` properties don't need to be specified in config. +> NOTE: When using the alias methods `url`, `method`, and `data` properties don't need to be specified in config. diff --git a/posts/en/sponsor.md b/posts/en/sponsor.md new file mode 100644 index 0000000..eb4f576 --- /dev/null +++ b/posts/en/sponsor.md @@ -0,0 +1,90 @@ +--- +title: 'Sponsoring Axios' +--- + +Thank you for considering supporting our project. Your donation will be used to maintain and develop Axios. + +As a benefit to our major sponsors, we provide the opportunity to add your logo and brief +information to our website and/or Readme.md depending on the tier of support you choose. +This process is fully automated if you donate through [OpenCollective.com](https://opencollective.com/axios/contribute), +so your logo will be added within 24 hours. + +Your logo can be placed: +- in the carousel on the main page +- in carousels on each page of the documentation +- at the top of Readme.md in our repo + +Place in the carousel depends on: +- newness of the sponsor (new sponsors temporarily receive higher places) +- selected support tier +- total amount of money donated +- consistency of donations + +If you donate through [GitHub](https://github.com/sponsors/axios), you will need to contact us later if you want your logo to be promoted according +to our support tiers. + +### Tiers + +See tiers list on [Open Collective](https://opencollective.com/axios/contribute) + +| | Bronze | Silver | Gold | Platinum | +|---------------------------------------------|:----------:|:-----------:|:-----------:|:----------------:| +| Main page | small logo | medium logo | large logo | extra large logo | +| Docs pages | | | medium logo | large logo | +| [Readme.md](https://github.com/axios/axios) | | | small logo | medium logo | +| Data merging from sponsor's Github repo | | + | + | + | +| Links block in tooltip* | | | + | + | +| Embedded Youtube video in tooltip* | | | + | + | +| Max description length in Readme.md (chars) | | | 100 | 150 | + +> Note: +> The extra links block and video can only be set via `sponsors.json` + +### Backer tier + +You can create a custom tier, in which case you will get the benefits of the highest existing tier whose price +is covered by your donation. The extra donation amount above the existing tier will be taken into account +when sorting sponsors in a carousel. + +### Sponsor logo + +Your logo will be downloaded to our server, optimized, trimmed empty borders and resized with preservation of proportions. +If the width of the logo is significantly greater than its height, then the text caption will be hidden, +and the logo will take up all the available space. The maximum logo height is the same for all tiers. + +### Description + +If the description is not provided, we will try to parse it from the sponsor's site meta tags. + +### GitHub + +If you have set up your GitHub profile in your Open Collective profile, +you can create a special repository called `axios-sponsor` with `sponsor.json` in its root to manage your sponsor profile data. + +The data from this file will be merged with your Open Collective profile which allows you to provide some extra info for advertising. + +`sponsor.json` has the following structure (each field is optional): + +```json + { + "displayName": "Umbrella Corporation", + "targetLink": "https://umbrellacorp.com/", + "alt": "Umbrella Corporation", + "image": "https://fake.com/logo.png", + "image_dark": "https://fake.com/logo_dark.png", + "description": "The Umbrella Corporation is a pharmaceutical company", + "website": "https://google.com/", + "github": "https://github.com/fakeGitHib", + "icon": "https://fake.com/icon.png", + "video": "https://www.youtube.com/embed/isosE4Bowh0", + "twitter": "https://x.com/profile", + "showCaption": true, + "crown": false, + "hide": false, + "links": { + "link1": "https://google.com/", + "link2": "https://google.com/" + } + } +``` +Every 24 hour our backend will pull this data when update sponsors list on the website. diff --git a/scripts/updateData.js b/scripts/updateData.js index 5724dc8..42571d3 100644 --- a/scripts/updateData.js +++ b/scripts/updateData.js @@ -5,13 +5,91 @@ const sharp = require('sharp'); const cheerio = require('cheerio'); const html = require('html-escaper'); const crypto = require('crypto'); +const Joi = require('joi'); +const showdown = require('showdown'); +const Handlebars = require('handlebars'); +const axios = require('axios'); -const axios = require("axios").create({ +const origin = 'https://axios-http.com/'; +//const origin = 'http://127.0.0.1:8080/'; + +const absoluteURI = (path) => new URL(path, origin); + +/*const converter = new showdown.Converter();*/ + +Handlebars.registerHelper("sep", function(options){ + if(options.data.last) { + return options.inverse(); + } else { + return options.fn(this); + } +}); + +Handlebars.registerHelper("short", function (...args) { + const options = args.pop(); + const [max = 50] = args; + + let s = options.fn(this)?.trim(); + + if (s.length > max) { + s = s.slice(0, max) + '...'; + } + + return new Handlebars.SafeString(s); +}); + +Handlebars.registerHelper("table", function(...args) { + const options = args.pop(); + const [context, columns = 1, separate] = args; + + const rows = [[]]; + let arr = rows[0]; + + const last = context.length - 1; + + const width = 100 / columns; + + context.forEach((that, i) => { + arr.push(`${options.fn(that)}`); + if (i !== last && arr.length === columns) { + rows.push(arr = []); + } + }); + + return new Handlebars + .SafeString(separate ? + rows.map(cells => `${cells.join('')}
`).join('') : + `${rows.map(cells => `${cells.join('')}`).join('')}
` + ); +}); + +const removeExtraLineBreaks = (str) => str.replace(/(?:\r\n|\r|\n){3,}/gm, '\r\n\r\n'); + +const cleanTemplate = template => template + .replace(/\s\s+/g, ' ') + .replace(/\n +/g, '\n') + .replace(/^ +/, '') + .replace(/\n\n\n+/g, '\n\n') + .replace(/\n\n$/, '\n'); + +const renderTemplate = async (data, template) => { + const compile = Handlebars.compile(String(await fs.readFile(template))) + + const content = compile(data); + + return removeExtraLineBreaks(cleanTemplate(content)); +} + + + +const axiosInstance = axios.create({ headers: { "User-Agent": 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' } }); + + const social = { website: 'website.png', facebook: 'facebook.png', @@ -31,10 +109,22 @@ const parseURL = (url) => { } } -const MONTH = 30 * 24 * 3600 * 1000; +const DAY = 24 * 3600; +const MONTH = 30 * DAY; +const PERIOD = 30; const readJSON = async (fileName) => JSON.parse(String(await fs.readFile(fileName))); const writeJSON = async (fileName, data) => await fs.writeFile(fileName, JSON.stringify(data, null, 2)); +const openJSON = async (filePath) => { + try { + return await readJSON(filePath); + } catch(err) { + if (err.code === 'ENOENT') { + return null; + } + throw err; + } +} const ensurePath = async (path) => { try { @@ -44,28 +134,34 @@ const ensurePath = async (path) => { } } -const makeUTMURL = (url, params, bypass) => { - const urlObj = new URL(url); - - const {searchParams} = urlObj; +const makeUTMURL = (url, bypass, params) => { + try { + const urlObj = new URL(url); + + const {searchParams} = urlObj; + + if(!bypass) { + Object.entries({ + utm_source: 'axios', + utm_medium: 'sponsorlist', + utm_campaign: 'sponsorship', + ...params + }).forEach(([param, value]) => { + !searchParams.has(param) && searchParams.set(param, value) + }); + } - if (!bypass && !searchParams.utm_source && !searchParams.utm_campaign) { - Object.entries({ - utm_source: 'axios', - utm_medium: 'sponsorlist', - utm_campaign: 'sponsorship', - ...params - }).forEach(([param, value]) => searchParams.set(param, value)); + return urlObj.toString(); + } catch (err) { + console.warn(`Failed to make UTM link for [${url}]: ${err}`); } - - return urlObj.toString(); } const getWithRetry = (url, retries = 3) => { let counter = 0; const doRequest = async () => { try { - return await axios.get(url) + return await axiosInstance.get(url) } catch (err) { if (counter++ >= retries) { throw err; @@ -81,15 +177,14 @@ const getWithRetry = (url, retries = 3) => { const pullSponsors = async (collective, {type = 'organizations'} = {}) => { const {data} = await getWithRetry(`https://opencollective.com/${collective}/members/${type}.json`); - return data.map(({lastTransactionAt, ...entry}) => { - const {profile} = entry; - + return data.map((sponsor) => { + const {profile} = sponsor; const login = new URL(profile).pathname.split('/').pop(); console.log(`Open Collective member: ${login}`); return { - ...entry, + ...sponsor, login } }); @@ -99,31 +194,37 @@ const processAvatars = async (sponsorsData, avatarsPath = './assets/sponsors/ope await ensurePath(avatarsPath); await Promise.all(Object.values(sponsorsData).map(async (sponsor) => { - const {image, profile} = sponsor; + const {image, profile, displayName} = sponsor; + + console.log(`Process avatar [${image}] for [${displayName}]`); try { if (!/^https?:/.test(image)) { return; } - const {data, headers} = await axios.get(image, {responseType: 'arraybuffer'}); + const {data, headers} = await axiosInstance.get(image, {responseType: 'arraybuffer'}); - const login = new URL(profile).pathname.split('/').pop(); + const login = profile ? new URL(profile).pathname.split('/').pop() : undefined; const ext = mime.extension(headers.getContentType()) || ''; - const localAvatarPath = path.join(avatarsPath, `${login}${ext ? '.' + ext : ''}`); + const localAvatarPath = path.join(avatarsPath, `${login || displayName}${ext ? '.' + ext : ''}`); - sponsor.image = '/' + localAvatarPath; - - await sharp(data) + const sharpImage = await sharp(data) + .trim({ + background: {r: 255, g: 255, b: 255, alpha: 0} + }) .resize(400, 150, { fit: sharp.fit.inside, withoutEnlargement: true - }) - .png() - .toFile(localAvatarPath) + }); + + await sharpImage.png().toFile(localAvatarPath); + sponsor.image = '/' + localAvatarPath; + + console.log(`Avatar for [${displayName}] saved as [${sponsor.image}]`); } catch(err){ console.log(`Error while loading logo [${image}]: ${err}`); sponsor.image = ''; @@ -131,6 +232,23 @@ const processAvatars = async (sponsorsData, avatarsPath = './assets/sponsors/ope })); } +const addImageMetadata = async (sponsors) => { + await Promise.all(Object.values(sponsors).map(async (sponsor) => { + const {image} = sponsor; + if(!image) return; + try { + const {width, height, format} = await sharp('.' + image).metadata(); + + sponsor.imageWidth = width; + sponsor.imageHeight = height; + sponsor.isWideImage = sponsor.showCaption === undefined && width > height * 1.8; + sponsor.imageType = format; + } catch(err) { + console.log(`Error while reading image metadata [${image}]: ${err}`); + } + })); +} + const downloadImage = async (src, maxWidth = 32, maxHeight = 32, imagesPath = './assets/sponsors/') => { let buffer; let name, ext; @@ -139,12 +257,12 @@ const downloadImage = async (src, maxWidth = 32, maxHeight = 32, imagesPath = '. try { if (/^https?:/.test(src)) { - const {data, headers} = await axios.get(src, {responseType: 'arraybuffer'}); + const {data, headers} = await axiosInstance.get(src, {responseType: 'arraybuffer'}); name = new URL(src).pathname.split('/').pop(); ext = mime.extension(headers.getContentType()) || ''; buffer = data; } else { - buffer = await fs.readFile(src); + buffer = await fs.readFile(src.replace(/^\//, './')); ext = path.extname(src); name = path.basename(src, ext); ext = ext.replace(/^\./, ''); @@ -174,7 +292,7 @@ const downloadImage = async (src, maxWidth = 32, maxHeight = 32, imagesPath = '. const getPageDescription = async (website) => { try { - const {data} = await axios.get(website); + const {data} = await axiosInstance.get(website); const $ = cheerio.load(data); return $('head > meta[name=description]').first().attr('content') || $('head > meta[name=title]').first().attr('content'); @@ -183,142 +301,466 @@ const getPageDescription = async (website) => { } } -const processSponsors = async (sponsorsData, sponsorsConfig = './data/sponsors.json') => { - const computedSponsors = {}; +const fitInRect = (w, h, mw = w, mh = h) => { + const ws = w / mw; + const hs = h / mh; + + if (ws > hs) { + return ws > 1 ? [mw, Math.round(h / ws)] : [w, h]; + } else { + return hs > 1 ? [Math.round(w / hs), mh] : [w, h]; + } +} + +const renderMarkdownSponsors = async (sponsors) => { + const render = async (sponsors, caption = 'Sponsors', cells, width = 300, height= 70, separate = false) => { + + return await renderTemplate({ + caption, + cells, + separate, + sponsors: sponsors.map(sponsor => { + const [w = 0, h = 0] = sponsor.image ? fitInRect(sponsor.imageWidth, sponsor.imageHeight, width, height) : []; + + const links = {}; + + sponsor.links && Object.entries(sponsor.links).forEach(([name, href]) =>{ + links[name] = href ? makeUTMURL(href, { + utm_source: 'axios', + utm_medium: 'readme_sponsorlist', + utm_campaign: 'sponsorship', + }, !sponsor.autoUTMLinks) : ''; + }); + + return { + ...sponsor, + links, + image: sponsor.image && absoluteURI(sponsor.image), + image_black: sponsor.image && absoluteURI(sponsor.image_black), + readmeImageWidth: w, + readmeImageHeight: h + }; + }), + }, './templates/sponsors.hbs'); + } + + const filterSponsors = (fn) => Object.values(sponsors).filter(fn); + + const rendered = []; + + rendered.push(await render(filterSponsors(({benefits, isActive, tierId}) => { + return isActive && benefits.readme && tierId === 'platinum'; + }), '💎 Platinum sponsors', 1, 300, 90, true)); + + rendered.push(await render(filterSponsors(({benefits, isActive, tierId}) => { + return isActive && benefits.readme && tierId === 'gold'; + }), '🥇 Gold sponsors', 3, 200, 70)); + + rendered.push(await render(filterSponsors(({benefits, isActive, tierId}) => { + return isActive && benefits.readme && tierId === 'silver'; + }), '🥈 Silver sponsors', 4, 150, 50)); + + return rendered.join('\n'); +} + +/*const updateReadmeSponsors = async (dest, path, sponsors, marker = '

') => { + let {data} = await getWithRetry(`https://api.github.com/repos/axios/axios/contents/${dest}`); + + let content = Buffer.from(data.content, 'base64').toString(); + + const index = content.indexOf(marker); + + if(index >= 0) { + const sponsorBlock = await renderMarkdownSponsors(sponsors) + + content = sponsorBlock + '\n' + content.slice(index); + + await fs.writeFile(path, content); + } else { + console.warn(`Can not find marker (${marker}) in ${uri}`); + } +};*/ + + + + + + + + + + + + + + +const schema = Joi.object({ + github: Joi.string().alphanum().max(255), + displayName: Joi.string().alphanum().min(1).max(64), + website: Joi.string().alphanum().max(255), + alt: Joi.string().alphanum().max(255), + icon: Joi.string().alphanum().max(255), + image: Joi.string().alphanum().max(255), + image_dark: Joi.string().alphanum().max(255), + targetLink: Joi.string().alphanum().max(255), + twitter: Joi.string().alphanum().max(255), + video: Joi.string().alphanum().max(255), + description: Joi.string().alphanum().max(1000), + links: Joi.object({}).unknown(true), + showCaption: Joi.boolean(), + crown: Joi.boolean(), + hide: Joi.boolean() +}).unknown(true); + +const processGithub = async (sponsor, repo = 'axios-sponsor', file = 'sponsor.json') => { + let {github, displayName} = sponsor; + + console.log(`Process github (${github}) of ${displayName}...`); + + if (github && (github = github.trim())) { + try { + const url = new URL(github); + if (url.hostname === 'github.com') { + const [githubUser] = url.pathname.slice(1).split('/'); + + if (githubUser) { + const targetURI = `https://raw.githubusercontent.com/${githubUser}/${repo}/main/${file}`; + + console.log(`Pull sponsor content from [${targetURI}]`); + + const {data} = await axiosInstance.get(targetURI); + + schema.validate(data); + + ['github', 'displayName', 'website', 'alt', 'image', 'image_dark', 'description', 'icon', 'video', 'twitter', 'targetLink'] + .forEach(key => { + if (data[key] !== undefined) { + sponsor[key] = String(data[key]); + } + }); + + data.links && (sponsor.links = data.links); + + 'showCaption' in data && (sponsor.showCaption = !!data.showCaption); + data.crown === false && (sponsor.benefits.crown = false); + data.hide === true && (sponsor.hide = true); + } + } + } catch (err) { + console.warn(String(err)); + } + } +} + +const addMonths = (date, months) => { + const d = date.getDate(); + date.setMonth(+months + date.getMonth()); + date.getDate() !== d && date.setDate(0); + return date; +} + + + +const renderTooltip = async (sponsor) => { + let {icon, isActive, displayName, tier, associatedTierId, lastTransactionAmount, price, description, website, benefits, video, autoUTMLinks, links} = sponsor; + + const iconSrc = icon && (await downloadImage(icon)); + + const iconHTML = iconSrc ? `` : ''; + + const renderedTier = isActive && tier.toLowerCase() === 'backer' ? `${price || lastTransactionAmount || 0}$ a month` : tier; + + let tooltip = `

${iconHTML}${html.escape(displayName)} (${sponsor.totalAmountDonated || 0}$${' ' + renderedTier + ''})

`; + + if (!description && website) { + description = sponsor.description = await getPageDescription(website); + + console.log(`Website ${website} description: ${description}`); + } + + if (description) { + tooltip += `
${html.escape(description)}
`; + } + + if (benefits.video && video) { + try { + const {hostname} = new URL(video); + + if (/youtube.com$/.test(hostname)) { + tooltip += `
`; + } + } catch (e) { + console.warn(`Failed to include youtube link: ${e}`); + } + } + + const linksArray = Object.entries(links || {}); + + if (benefits.links && linksArray.length && benefits.links) { + const rendered = linksArray.slice(0, benefits.links).map(([text, entry]) => { + const {href} = typeof entry === 'string' ? { + href: entry + } : entry || {}; + + return `${html.escape(text)}`; + }).join(''); + + tooltip += `` + } + + const icons = Object.entries(social).map(([name, icon]) => { + const link = sponsor[name]; + + if(!link) return; + + return ``; + }).filter(Boolean).join(''); + + tooltip += `
${icons}
` + + return tooltip; +} + +const fitInRange = (v, min, max) => Math.max(Math.min(v, max), min); + +/*const mapObject = (obj, fn) => { + const newObj = {}; + + Object.entries(obj).forEach((key, value) => { + const ret = fn(key, value, obj); + + if() + }); +}*/ + +const findTier = (price, tiers) => { + let found; + let max = 0; + + price && Object.entries(tiers).forEach(([tier, data]) => { + if (data.price <= price && max < price) { + max = data.price; + found = tier; + } + }); + + return found; +} + +const processSponsors = async (collectiveSponsors, sponsorsConfig = './data/sponsors.json') => { + const { sponsors, tiers, - totalAmountDonatedThreshold = 100, - monthlyContributionThreshold = 10, - disappearCredit = 10, scoreTierPriceFactor = 0.5, - scoreTotalAmountFactor = 0.2 + scoreTotalAmountFactor = 0.2, + creditDays = 0 } = await readJSON(sponsorsConfig) || {}; + const mergedSponsors = {}; + // merge Open Collective sponsors - sponsorsData.forEach(sponsor => { - if (sponsor.role !== 'BACKER' && sponsor.role && sponsor.totalAmountDonated <= 0) { + collectiveSponsors.forEach(sponsor => { + if (sponsor.role !== 'BACKER' && sponsor.role) { return; } - computedSponsors[sponsor.login] = {...sponsor}; + const {isActive, totalAmountDonated, lastTransactionAmount} = sponsor; + + sponsor.isActive = !!(isActive && lastTransactionAmount > 0 && totalAmountDonated); + + mergedSponsors[sponsor.login] = {...sponsor}; }); - // merge config sponsors - Object.entries(sponsors).forEach(([login, entry]) => { - const existing = computedSponsors[login] || {}; + // merge sponsors from sponsors.json + Object.entries(sponsors).forEach(([login, local]) => { + let collective = mergedSponsors[login]; - computedSponsors[login] = {login, ...existing, ...entry}; + mergedSponsors[login] = { + login, + ...collective, + ...local, + localConfig: {...local}, + manualBilling: !!(local.lastTransactionAt && local.lastTransactionAmount || local.endDate) + } }); - await Promise.all(Object.values(computedSponsors).map(async (sponsor) => { - let {login, icon, website, displayName, description, links} = sponsor; - sponsor.displayName = displayName = displayName || sponsor.name || login; - console.log(`Process sponsor [${displayName}]`); + // normalize sponsors config + Object.entries(mergedSponsors).forEach(([login, sponsor]) => { + if (!sponsor.tier) { + sponsor.tier = 'backer'; + } + + let {isActive, tier, lastTransactionAmount = 0, createdAt, lastTransactionAt, totalAmountDonated, manualBilling, localConfig} = sponsor; + + const tierLower = tier.toLowerCase(); + + const tierData = tiers[tierLower]; + + if (!tierData) { + console.log(`Unknown tier [${tier}]`); + } + + const {price, benefits, period = PERIOD} = tierData || {}; + + console.log(sponsor.login); + + const isBacker = tierLower === 'backer'; - const iconHTML = icon ? `` : ''; + sponsor.tierId = tierLower; + sponsor.tierPrice = isBacker && isActive && lastTransactionAmount ? lastTransactionAmount : price; + sponsor.totalAmountDonated = sponsor.totalAmountDonated || lastTransactionAmount || 0; - let tooltip = `

${iconHTML}${displayName} (${sponsor.totalAmountDonated}$${sponsor.tier && sponsor.isActive ? ' ' + sponsor.tier + '' : ''})

`; + sponsor.associatedTierId = tierLower; - if (!description && website) { - description = await getPageDescription(website); - console.log(`Website ${website} description: ${description}`); + let assoc; + + if (localConfig?.isActive === false) { + sponsor.isActive = false; + } else if (tier && (manualBilling || tierLower === 'backer')) { + if(sponsor.endDate || lastTransactionAmount >= price) { + const pricePerSec = price / (period * DAY); + const timePaid = lastTransactionAmount / pricePerSec + const endDate = sponsor.endDate = sponsor.endDate || +(new Date(lastTransactionAt)) + timePaid * 1000; + const timeLeft = new Date(endDate) - Date.now(); + + sponsor.timeLeft = Math.round(timeLeft / 1000); // seconds + sponsor.isActive = sponsor.timeLeft > 0; + } else { + sponsor.isActive = false; + } } - if (description) { - tooltip += `
${description}
`; + if( Date.now() - new Date(sponsor.boostEnd) > 0) { + sponsor.boost = sponsor.boost || 1; } - sponsor.targetLink = website || sponsor.twitter || sponsor.github || sponsor.profile; + if (isActive && isBacker && !localConfig?.tier && (assoc = findTier(lastTransactionAmount, tiers))) { + const tier = assoc.toLowerCase(); + tier !== 'backer' && (sponsor.associatedTierId = tier); + } - const autoUTMLinks = sponsor.autoUTMLinks !== false; // hotfix + sponsor.isActive = sponsor.isActive === true; - const linksArray = Object.entries(links || {}); + sponsor.benefits = { + ...benefits, + ...sponsor.benefits + }; - if (linksArray.length) { - const rendered = linksArray.map(([text, entry]) => { - const {href} = typeof entry === 'string' ? { - href: entry - } : entry || {}; + sponsor.autoUTMLinks = sponsor.autoUTMLinks !== false; - return `${html.escape(text)}`; - }).join(''); + let displayName = sponsor.displayName || sponsor.name || sponsor.login || login; - tooltip += `