diff --git a/package-lock.json b/package-lock.json index 4f8bbab..2454039 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dedpaste", - "version": "1.12.0", + "version": "1.14.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dedpaste", - "version": "1.12.0", + "version": "1.14.1", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -18,13 +18,15 @@ "@types/marked": "^5.0.2", "clipboardy": "^4.0.0", "commander": "^13.1.0", + "eslint": "^9.34.0", "highlight.js": "^11.11.1", "inquirer": "^9.2.15", "keybase-api": "^0.0.1", "marked": "^16.2.0", "mime-types": "^2.1.35", "node-fetch": "^3.3.2", - "openpgp": "^6.1.0" + "openpgp": "^6.1.0", + "prettier": "^3.6.2" }, "bin": { "dedpaste": "cli/index.js" @@ -1320,6 +1322,193 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz", + "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==", + "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" + } + }, + "node_modules/@eslint-community/eslint-utils/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==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "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/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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -1330,6 +1519,54 @@ "node": ">=14" } }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -2557,6 +2794,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -2611,6 +2854,12 @@ "@types/istanbul-lib-report": "*" } }, + "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==", + "license": "MIT" + }, "node_modules/@types/marked": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", @@ -2699,7 +2948,6 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2708,6 +2956,15 @@ "node": ">=0.4.0" } }, + "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==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", @@ -2718,6 +2975,22 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "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" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -2785,7 +3058,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/as-table": { @@ -2952,7 +3224,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -3577,7 +3848,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/concurrently": { @@ -3816,6 +4086,12 @@ "node": ">=6" } }, + "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==", + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -4033,6 +4309,157 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "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-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "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-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==", + "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/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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "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/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4047,6 +4474,48 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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==", + "license": "BSD-2-Clause", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -4131,11 +4600,22 @@ "node": ">=4" } }, + "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==", + "license": "MIT" + }, "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, + "license": "MIT" + }, + "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==", "license": "MIT" }, "node_modules/fb-watchman": { @@ -4171,6 +4651,18 @@ "node": "^12.20 || >= 14.13" } }, + "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==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4193,7 +4685,6 @@ "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, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -4216,6 +4707,25 @@ "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==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4513,6 +5023,15 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4560,7 +5079,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -4661,7 +5179,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4690,7 +5207,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5614,7 +6130,6 @@ "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" @@ -5635,12 +6150,30 @@ "node": ">=6" } }, + "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==", + "license": "MIT" + }, "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==", "license": "MIT" }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5660,6 +6193,15 @@ "integrity": "sha512-SqUoj+4XeZon3J92fhCs+cSlrC7oL+u2tOtG4gXy81250FPUKwaQZtdn6SK5ObDpdXlw7tn0JWYnVxaB7YWyyg==", "license": "GPL-3.0-or-later" }, + "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==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -5680,6 +6222,19 @@ "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==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -5690,7 +6245,6 @@ "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, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -5709,6 +6263,12 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -5976,7 +6536,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, "license": "MIT" }, "node_modules/node-domexception": { @@ -6117,6 +6676,23 @@ "node": ">= 18.0.0" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -6153,7 +6729,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -6169,7 +6744,6 @@ "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, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -6224,7 +6798,6 @@ "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, "license": "MIT", "engines": { "node": ">=8" @@ -6385,6 +6958,30 @@ "node": ">=8" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -6454,6 +7051,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -6953,7 +7559,6 @@ "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, "license": "MIT", "engines": { "node": ">=8" @@ -7151,6 +7756,18 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "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==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -7259,6 +7876,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7334,6 +7960,15 @@ "node": ">= 8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerd": { "version": "1.20250604.0", "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250604.0.tgz", @@ -7531,7 +8166,6 @@ "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, "license": "MIT", "engines": { "node": ">=10" diff --git a/package.json b/package.json index 4d11b94..8b3621e 100644 --- a/package.json +++ b/package.json @@ -65,12 +65,14 @@ "@types/marked": "^5.0.2", "clipboardy": "^4.0.0", "commander": "^13.1.0", + "eslint": "^9.34.0", "highlight.js": "^11.11.1", "inquirer": "^9.2.15", "keybase-api": "^0.0.1", "marked": "^16.2.0", "mime-types": "^2.1.35", "node-fetch": "^3.3.2", - "openpgp": "^6.1.0" + "openpgp": "^6.1.0", + "prettier": "^3.6.2" } } diff --git a/src/index.ts b/src/index.ts index 2d81623..20d349f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,46 +1,46 @@ -import { marked } from 'marked'; -import hljs from 'highlight.js/lib/core'; +import { marked } from "marked"; +import hljs from "highlight.js/lib/core"; // Import only the languages we want to support to keep bundle size down -import javascript from 'highlight.js/lib/languages/javascript'; -import typescript from 'highlight.js/lib/languages/typescript'; -import python from 'highlight.js/lib/languages/python'; -import json from 'highlight.js/lib/languages/json'; -import xml from 'highlight.js/lib/languages/xml'; // HTML, XML -import css from 'highlight.js/lib/languages/css'; -import markdown from 'highlight.js/lib/languages/markdown'; -import bash from 'highlight.js/lib/languages/bash'; -import yaml from 'highlight.js/lib/languages/yaml'; -import sql from 'highlight.js/lib/languages/sql'; -import java from 'highlight.js/lib/languages/java'; -import cpp from 'highlight.js/lib/languages/cpp'; -import go from 'highlight.js/lib/languages/go'; -import rust from 'highlight.js/lib/languages/rust'; -import ruby from 'highlight.js/lib/languages/ruby'; -import php from 'highlight.js/lib/languages/php'; +import javascript from "highlight.js/lib/languages/javascript"; +import typescript from "highlight.js/lib/languages/typescript"; +import python from "highlight.js/lib/languages/python"; +import json from "highlight.js/lib/languages/json"; +import xml from "highlight.js/lib/languages/xml"; // HTML, XML +import css from "highlight.js/lib/languages/css"; +import markdown from "highlight.js/lib/languages/markdown"; +import bash from "highlight.js/lib/languages/bash"; +import yaml from "highlight.js/lib/languages/yaml"; +import sql from "highlight.js/lib/languages/sql"; +import java from "highlight.js/lib/languages/java"; +import cpp from "highlight.js/lib/languages/cpp"; +import go from "highlight.js/lib/languages/go"; +import rust from "highlight.js/lib/languages/rust"; +import ruby from "highlight.js/lib/languages/ruby"; +import php from "highlight.js/lib/languages/php"; // Import MUI styles -import { getHomepageHTML, getMuiCSS, getMarkdownStyles } from './muiStyles'; +import { getHomepageHTML, getMuiCSS, getMarkdownStyles } from "./muiStyles"; // Register languages with highlight.js -hljs.registerLanguage('javascript', javascript); -hljs.registerLanguage('typescript', typescript); -hljs.registerLanguage('python', python); -hljs.registerLanguage('json', json); -hljs.registerLanguage('xml', xml); -hljs.registerLanguage('html', xml); // HTML is handled by XML -hljs.registerLanguage('css', css); -hljs.registerLanguage('markdown', markdown); -hljs.registerLanguage('bash', bash); -hljs.registerLanguage('sh', bash); // Shell alias -hljs.registerLanguage('yaml', yaml); -hljs.registerLanguage('sql', sql); -hljs.registerLanguage('java', java); -hljs.registerLanguage('cpp', cpp); -hljs.registerLanguage('c++', cpp); // C++ alias -hljs.registerLanguage('go', go); -hljs.registerLanguage('rust', rust); -hljs.registerLanguage('ruby', ruby); -hljs.registerLanguage('php', php); +hljs.registerLanguage("javascript", javascript); +hljs.registerLanguage("typescript", typescript); +hljs.registerLanguage("python", python); +hljs.registerLanguage("json", json); +hljs.registerLanguage("xml", xml); +hljs.registerLanguage("html", xml); // HTML is handled by XML +hljs.registerLanguage("css", css); +hljs.registerLanguage("markdown", markdown); +hljs.registerLanguage("bash", bash); +hljs.registerLanguage("sh", bash); // Shell alias +hljs.registerLanguage("yaml", yaml); +hljs.registerLanguage("sql", sql); +hljs.registerLanguage("java", java); +hljs.registerLanguage("cpp", cpp); +hljs.registerLanguage("c++", cpp); // C++ alias +hljs.registerLanguage("go", go); +hljs.registerLanguage("rust", rust); +hljs.registerLanguage("ruby", ruby); +hljs.registerLanguage("php", php); // Define the environment interface for Cloudflare Workers type Env = { @@ -64,8 +64,9 @@ const ONE_TIME_PREFIX = "onetime-"; // Generate a random ID for the paste function generateId(length = 8): string { - const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - let result = ''; + const chars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let result = ""; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } @@ -86,7 +87,7 @@ interface ViewedPasteTracker { const viewedPastes: ViewedPasteTracker = {}; // KV namespace key for tracking viewed pastes (to persist across worker restarts/instances) -const VIEWED_PASTES_KEY = 'viewed_pastes_registry'; +const VIEWED_PASTES_KEY = "viewed_pastes_registry"; // Helper function to safely parse JSON with a default value function safeJsonParse(jsonString: string | null, defaultValue: T): T { @@ -94,67 +95,75 @@ function safeJsonParse(jsonString: string | null, defaultValue: T): T { try { return JSON.parse(jsonString) as T; } catch (e) { - console.error('Error parsing JSON:', e); + console.error("Error parsing JSON:", e); return defaultValue; } } export default { - async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + async fetch( + request: Request, + env: Env, + ctx: ExecutionContext, + ): Promise { // Initialize viewedPastes from KV store if available (completely optional enhancement) try { if (env.PASTE_METADATA) { - const storedViewedPastes = await env.PASTE_METADATA.get(VIEWED_PASTES_KEY); + const storedViewedPastes = + await env.PASTE_METADATA.get(VIEWED_PASTES_KEY); if (storedViewedPastes) { - const parsedPastes = safeJsonParse(storedViewedPastes, {}); + const parsedPastes = safeJsonParse( + storedViewedPastes, + {}, + ); // Merge with any in-memory pastes (newer ones take precedence) Object.assign(viewedPastes, parsedPastes); } } } catch (error) { // Log but continue without error - KV is an enhancement, not a requirement - console.log('[KV] Optional paste metadata storage not available:', error); + console.log("[KV] Optional paste metadata storage not available:", error); } const url = new URL(request.url); const path = url.pathname; // Handle OPTIONS requests for CORS - if (request.method === 'OPTIONS') { + if (request.method === "OPTIONS") { return new Response(null, { headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type', + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", }, }); } // Upload a new paste - if (request.method === 'POST') { + if (request.method === "POST") { // Handle regular uploads - if (path === '/upload' || path === '/temp') { - const isOneTime = path === '/temp'; + if (path === "/upload" || path === "/temp") { + const isOneTime = path === "/temp"; return await handleUpload(request, env, isOneTime, false); } - + // Handle encrypted uploads - if (path === '/e/upload' || path === '/e/temp') { - const isOneTime = path === '/e/temp'; + if (path === "/e/upload" || path === "/e/temp") { + const isOneTime = path === "/e/temp"; return await handleUpload(request, env, isOneTime, true); } - - return new Response('Not found', { status: 404 }); + + return new Response("Not found", { status: 404 }); } // Get a paste - if (request.method === 'GET') { + if (request.method === "GET") { // Handle regular pastes const regularMatch = path.match(/^\/([a-zA-Z0-9]{8})$/); if (regularMatch) { const id = regularMatch[1]; return await handleGet(id, env, ctx, false, request); } - + // Handle encrypted pastes const encryptedMatch = path.match(/^\/e\/([a-zA-Z0-9]{8})$/); if (encryptedMatch) { @@ -163,33 +172,33 @@ export default { } // Try to serve styles.css directly if [site] configuration doesn't work - if (path === '/styles.css') { + if (path === "/styles.css") { // Serve MUI CSS instead of Tailwind const cssContent = getMuiCSS(); - - console.log('Serving MUI CSS file'); + + console.log("Serving MUI CSS file"); return new Response(cssContent, { headers: { - 'Content-Type': 'text/css', - 'Cache-Control': 'public, max-age=86400', + "Content-Type": "text/css", + "Cache-Control": "public, max-age=86400", }, }); } - + // Serve the HTML homepage - if (path === '/') { + if (path === "/") { return new Response(generateHomepage(url.origin), { headers: { - 'Content-Type': 'text/html', - 'Access-Control-Allow-Origin': '*', + "Content-Type": "text/html", + "Access-Control-Allow-Origin": "*", }, }); } - return new Response('Not found', { status: 404 }); + return new Response("Not found", { status: 404 }); } - return new Response('Method not allowed', { status: 405 }); + return new Response("Method not allowed", { status: 405 }); }, }; @@ -488,40 +497,47 @@ curl ${origin}/e/{paste-id} */ } -async function handleUpload(request: Request, env: Env, isOneTime: boolean, isEncrypted: boolean): Promise { - const contentType = request.headers.get('Content-Type') || 'text/plain'; +async function handleUpload( + request: Request, + env: Env, + isOneTime: boolean, + isEncrypted: boolean, +): Promise { + const contentType = request.headers.get("Content-Type") || "text/plain"; const content = await request.arrayBuffer(); - + // Check if the content is empty if (content.byteLength === 0) { - return new Response('Content cannot be empty', { status: 400 }); + return new Response("Content cannot be empty", { status: 400 }); } - + // Generate a unique ID for the paste let id = generateId(); - + // Ensure encrypted content is always stored with the correct content type // This fixes issues with one-time encrypted pastes - const adjustedContentType = isEncrypted ? 'application/json' : contentType; - + const adjustedContentType = isEncrypted ? "application/json" : contentType; + // For one-time pastes, use a completely different storage strategy with a prefix if (isOneTime) { // Add a prefix to clearly identify one-time pastes const storageKey = `${ONE_TIME_PREFIX}${id}`; - + // Create the metadata for the paste const metadata: PasteMetadata = { contentType: adjustedContentType, isOneTime: true, // Always true for this storage path createdAt: Date.now(), }; - + // Store the content in R2 with the prefixed key await env.PASTE_BUCKET.put(storageKey, content, { customMetadata: metadata as any, }); - - console.log(`Created one-time paste with storage key ${storageKey}, isEncrypted=${isEncrypted}`); + + console.log( + `Created one-time paste with storage key ${storageKey}, isEncrypted=${isEncrypted}`, + ); } else { // Regular paste - standard storage path // Create the metadata for the paste @@ -530,15 +546,15 @@ async function handleUpload(request: Request, env: Env, isOneTime: boolean, isEn isOneTime: false, // Always false for this storage path createdAt: Date.now(), }; - + // Store the content in R2 with metadata await env.PASTE_BUCKET.put(id, content, { customMetadata: metadata as any, }); - + console.log(`Created regular paste ${id}, isEncrypted=${isEncrypted}`); } - + const baseUrl = new URL(request.url).origin; // Generate URL with /e/ prefix for encrypted pastes const pasteUrl = isEncrypted ? `${baseUrl}/e/${id}` : `${baseUrl}/${id}`; @@ -546,140 +562,183 @@ async function handleUpload(request: Request, env: Env, isOneTime: boolean, isEn // Return the paste URL - we always use the unprefixed ID in the URL return new Response(pasteUrl, { headers: { - 'Access-Control-Allow-Origin': '*', - 'Content-Type': 'text/plain', + "Access-Control-Allow-Origin": "*", + "Content-Type": "text/plain", }, }); } -async function handleGet(id: string, env: Env, ctx: ExecutionContext, isEncrypted: boolean, request: Request): Promise { +async function handleGet( + id: string, + env: Env, + ctx: ExecutionContext, + isEncrypted: boolean, + request: Request, +): Promise { // Check for raw parameter to bypass markdown rendering const url = new URL(request.url); - const wantsRaw = url.searchParams.get('raw') === 'true'; + const wantsRaw = url.searchParams.get("raw") === "true"; // First, check if this is a one-time paste by trying to get it with the one-time prefix const oneTimeKey = `${ONE_TIME_PREFIX}${id}`; - console.log(`[GET] Checking for one-time paste with key: ${oneTimeKey}, isEncrypted=${isEncrypted}`); - + console.log( + `[GET] Checking for one-time paste with key: ${oneTimeKey}, isEncrypted=${isEncrypted}`, + ); + // Add onlyIf condition to bust caches const oneTimePaste = await env.PASTE_BUCKET.get(oneTimeKey); - + // If we found a one-time paste with the prefixed key if (oneTimePaste) { // Check if this paste has already been viewed in this instance if (oneTimeKey in viewedPastes) { - console.log(`[TEMP PASTE] Paste already viewed and pending deletion: ${id}, key=${oneTimeKey}`); - + console.log( + `[TEMP PASTE] Paste already viewed and pending deletion: ${id}, key=${oneTimeKey}`, + ); + // Double-check by trying to delete it again, just to be sure try { await env.PASTE_BUCKET.delete(oneTimeKey); } catch (e) { // Ignore errors } - - return new Response('This one-time paste has already been viewed and is no longer available.', { - status: 404, - headers: { - 'Content-Type': 'text/plain', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0', - 'Surrogate-Control': 'no-store', - 'Pragma': 'no-cache', - 'Expires': '0', - 'X-One-Time': 'true', - 'X-Already-Viewed': 'true' - } - }); + + return new Response( + "This one-time paste has already been viewed and is no longer available.", + { + status: 404, + headers: { + "Content-Type": "text/plain", + "Cache-Control": + "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0", + "Surrogate-Control": "no-store", + Pragma: "no-cache", + Expires: "0", + "X-One-Time": "true", + "X-Already-Viewed": "true", + }, + }, + ); } - + // Mark this paste as viewed with timestamp viewedPastes[oneTimeKey] = { viewedAt: Date.now(), deleted: false, - attempts: 0 + attempts: 0, }; - + // Store in KV if available (optional enhancement) if (env.PASTE_METADATA) { try { - await env.PASTE_METADATA.put(VIEWED_PASTES_KEY, JSON.stringify(viewedPastes)); + await env.PASTE_METADATA.put( + VIEWED_PASTES_KEY, + JSON.stringify(viewedPastes), + ); } catch (error) { // Non-blocking error - the in-memory tracking still works console.log(`[KV] Optional metadata storage unavailable: ${error}`); } } - console.log(`[TEMP PASTE] Found one-time paste with ID: ${id}, isEncrypted=${isEncrypted}`); - + console.log( + `[TEMP PASTE] Found one-time paste with ID: ${id}, isEncrypted=${isEncrypted}`, + ); + // Get the content and metadata before we delete the paste const content = await oneTimePaste.arrayBuffer(); - let contentType = 'text/plain'; - let filename = ''; - + let contentType = "text/plain"; + let filename = ""; + try { const metadata = oneTimePaste.customMetadata as unknown as PasteMetadata; - contentType = metadata.contentType || 'text/plain'; - filename = metadata.filename || ''; - console.log(`[TEMP PASTE] Content type from metadata: ${contentType}, filename: ${filename}`); + contentType = metadata.contentType || "text/plain"; + filename = metadata.filename || ""; + console.log( + `[TEMP PASTE] Content type from metadata: ${contentType}, filename: ${filename}`, + ); } catch (err) { - console.error(`[TEMP PASTE] Error retrieving metadata for one-time paste ${id}: ${err}`); + console.error( + `[TEMP PASTE] Error retrieving metadata for one-time paste ${id}: ${err}`, + ); } - + // Override content type if this is an encrypted paste but the content type doesn't match // This ensures proper decryption on the client side - if (isEncrypted && contentType !== 'application/json') { - console.log(`[TEMP PASTE] Overriding content type for encrypted paste from ${contentType} to application/json`); - contentType = 'application/json'; + if (isEncrypted && contentType !== "application/json") { + console.log( + `[TEMP PASTE] Overriding content type for encrypted paste from ${contentType} to application/json`, + ); + contentType = "application/json"; } - + // Delete the paste immediately before returning the content try { // First deletion attempt - force immediate deletion await env.PASTE_BUCKET.delete(oneTimeKey); - console.log(`[TEMP PASTE] First deletion attempt for one-time paste with ID: ${id}, key=${oneTimeKey}, isEncrypted=${isEncrypted}`); - + console.log( + `[TEMP PASTE] First deletion attempt for one-time paste with ID: ${id}, key=${oneTimeKey}, isEncrypted=${isEncrypted}`, + ); + // Important: Add a small delay to allow propagation in Cloudflare's systems - await new Promise(resolve => setTimeout(resolve, 50)); - + await new Promise((resolve) => setTimeout(resolve, 50)); + // Second deletion attempt to ensure consistency await env.PASTE_BUCKET.delete(oneTimeKey); - console.log(`[TEMP PASTE] Second deletion attempt for one-time paste with ID: ${id}, key=${oneTimeKey}, isEncrypted=${isEncrypted}`); - + console.log( + `[TEMP PASTE] Second deletion attempt for one-time paste with ID: ${id}, key=${oneTimeKey}, isEncrypted=${isEncrypted}`, + ); + // Verify the deletion worked const verifyDeletion = await env.PASTE_BUCKET.get(oneTimeKey); if (verifyDeletion) { - console.error(`[TEMP PASTE] Warning: Failed to delete one-time paste: ${id}, key=${oneTimeKey}`); + console.error( + `[TEMP PASTE] Warning: Failed to delete one-time paste: ${id}, key=${oneTimeKey}`, + ); // Even though deletion appears to have failed, mark it as viewed in our tracking // system to prevent subsequent access viewedPastes[oneTimeKey].deleted = false; viewedPastes[oneTimeKey].attempts++; - + // Store updated tracking in KV if available if (env.PASTE_METADATA) { try { - await env.PASTE_METADATA.put(VIEWED_PASTES_KEY, JSON.stringify(viewedPastes)); + await env.PASTE_METADATA.put( + VIEWED_PASTES_KEY, + JSON.stringify(viewedPastes), + ); } catch (kvError) { // Non-blocking - in-memory tracking still works console.log(`[KV] Optional metadata update failed: ${kvError}`); } } - + // Force another deletion attempt await env.PASTE_BUCKET.delete(oneTimeKey); } else { - console.log(`[TEMP PASTE] Successfully deleted one-time paste with ID: ${id}, key=${oneTimeKey}`); + console.log( + `[TEMP PASTE] Successfully deleted one-time paste with ID: ${id}, key=${oneTimeKey}`, + ); viewedPastes[oneTimeKey].deleted = true; viewedPastes[oneTimeKey].attempts++; - + // Store updated tracking in KV try { if (env.PASTE_METADATA) { - await env.PASTE_METADATA.put(VIEWED_PASTES_KEY, JSON.stringify(viewedPastes)); + await env.PASTE_METADATA.put( + VIEWED_PASTES_KEY, + JSON.stringify(viewedPastes), + ); } } catch (kvError) { - console.error(`[TEMP PASTE] Error updating paste registry after successful deletion: ${kvError}`); + console.error( + `[TEMP PASTE] Error updating paste registry after successful deletion: ${kvError}`, + ); } } } catch (error) { - console.error(`[TEMP PASTE] Error deleting one-time paste with ID: ${id}: ${error}`); - + console.error( + `[TEMP PASTE] Error deleting one-time paste with ID: ${id}: ${error}`, + ); + // Schedule multiple backup deletion attempts to make sure it gets deleted ctx.waitUntil( (async () => { @@ -689,147 +748,172 @@ async function handleGet(id: string, env: Env, ctx: ExecutionContext, isEncrypte try { // Add delay between attempts with exponential backoff const delay = 200 * Math.pow(2, i); // 200ms, 400ms, 800ms, 1600ms, 3200ms - await new Promise(resolve => setTimeout(resolve, delay)); - console.log(`[TEMP PASTE] Backup deletion attempt ${i+1}/${deletionAttempts} for one-time paste ${id}, key=${oneTimeKey}`); + await new Promise((resolve) => setTimeout(resolve, delay)); + console.log( + `[TEMP PASTE] Backup deletion attempt ${i + 1}/${deletionAttempts} for one-time paste ${id}, key=${oneTimeKey}`, + ); await env.PASTE_BUCKET.delete(oneTimeKey); - + // Verify after each attempt const checkResult = await env.PASTE_BUCKET.get(oneTimeKey); if (!checkResult) { - console.log(`[TEMP PASTE] Backup deletion attempt ${i+1} successfully deleted one-time paste ${id}`); - + console.log( + `[TEMP PASTE] Backup deletion attempt ${i + 1} successfully deleted one-time paste ${id}`, + ); + // Update tracking with success viewedPastes[oneTimeKey].deleted = true; viewedPastes[oneTimeKey].attempts += 1; - + // Store updated tracking in KV if available if (env.PASTE_METADATA) { try { - await env.PASTE_METADATA.put(VIEWED_PASTES_KEY, JSON.stringify(viewedPastes)); + await env.PASTE_METADATA.put( + VIEWED_PASTES_KEY, + JSON.stringify(viewedPastes), + ); } catch (kvUpdateError) { // Non-blocking - in-memory tracking still works - console.log(`[KV] Optional metadata backup update failed: ${kvUpdateError}`); + console.log( + `[KV] Optional metadata backup update failed: ${kvUpdateError}`, + ); } } - + break; // Successfully deleted } } catch (backupError) { - console.error(`[TEMP PASTE] Backup deletion attempt ${i+1} failed for one-time paste ${id}: ${backupError}`); + console.error( + `[TEMP PASTE] Backup deletion attempt ${i + 1} failed for one-time paste ${id}: ${backupError}`, + ); } } - })() + })(), ); } - + // Check if this is markdown content that should be rendered as HTML - const isMarkdown = contentType === 'text/markdown' || - contentType === 'text/x-markdown' || - filename.endsWith('.md') || - filename.endsWith('.markdown'); - + const isMarkdown = + contentType === "text/markdown" || + contentType === "text/x-markdown" || + filename.endsWith(".md") || + filename.endsWith(".markdown"); + if (isMarkdown && !isEncrypted && !wantsRaw) { // Convert markdown to HTML for browser viewing const textContent = new TextDecoder().decode(content); - const renderedHTML = await renderMarkdownAsHTML(textContent, id, filename, true); - + const renderedHTML = await renderMarkdownAsHTML( + textContent, + id, + filename, + true, + ); + return new Response(renderedHTML, { headers: { - 'Content-Type': 'text/html; charset=utf-8', - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0', - 'CDN-Cache-Control': 'no-store', - 'Surrogate-Control': 'no-store', - 'Pragma': 'no-cache', - 'Expires': '0', - 'X-Original-Content-Type': contentType, - 'X-Rendered-Markdown': 'true', - 'X-One-Time': 'true', - 'X-Paste-Viewed-At': new Date().toISOString(), + "Content-Type": "text/html; charset=utf-8", + "Access-Control-Allow-Origin": "*", + "Cache-Control": + "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0", + "CDN-Cache-Control": "no-store", + "Surrogate-Control": "no-store", + Pragma: "no-cache", + Expires: "0", + "X-Original-Content-Type": contentType, + "X-Rendered-Markdown": "true", + "X-One-Time": "true", + "X-Paste-Viewed-At": new Date().toISOString(), }, }); } - + // Return the content with stronger cache control headers return new Response(content, { headers: { - 'Content-Type': contentType, - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0', - 'CDN-Cache-Control': 'no-store', // Additional CDN-specific directive - 'Surrogate-Control': 'no-store', // For Cloudflare and other CDNs - 'Pragma': 'no-cache', - 'Expires': '0', - 'X-Encrypted': isEncrypted ? 'true' : 'false', - 'X-One-Time': 'true', // Mark as one-time paste explicitly - 'X-Paste-Viewed-At': new Date().toISOString(), // Add timestamp of viewing + "Content-Type": contentType, + "Access-Control-Allow-Origin": "*", + "Cache-Control": + "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0", + "CDN-Cache-Control": "no-store", // Additional CDN-specific directive + "Surrogate-Control": "no-store", // For Cloudflare and other CDNs + Pragma: "no-cache", + Expires: "0", + "X-Encrypted": isEncrypted ? "true" : "false", + "X-One-Time": "true", // Mark as one-time paste explicitly + "X-Paste-Viewed-At": new Date().toISOString(), // Add timestamp of viewing }, }); } - + // If not a one-time paste or if it's already been retrieved (deleted), // check for a regular paste const paste = await env.PASTE_BUCKET.get(id); - + if (!paste) { - return new Response('Paste not found', { status: 404 }); + return new Response("Paste not found", { status: 404 }); } - + // Regular paste - get the content and metadata const content = await paste.arrayBuffer(); - let contentType = 'text/plain'; - let filename = ''; - + let contentType = "text/plain"; + let filename = ""; + try { const metadata = paste.customMetadata as unknown as PasteMetadata; - contentType = metadata.contentType || 'text/plain'; - filename = metadata.filename || ''; - console.log(`[REGULAR PASTE] Content type from metadata: ${contentType}, filename: ${filename}`); + contentType = metadata.contentType || "text/plain"; + filename = metadata.filename || ""; + console.log( + `[REGULAR PASTE] Content type from metadata: ${contentType}, filename: ${filename}`, + ); } catch (err) { console.error(`Error retrieving metadata for paste ${id}: ${err}`); } - + // Override content type if this is an encrypted paste but the content type doesn't match // This ensures proper decryption on the client side - if (isEncrypted && contentType !== 'application/json') { - console.log(`[REGULAR PASTE] Overriding content type for encrypted paste from ${contentType} to application/json`); - contentType = 'application/json'; + if (isEncrypted && contentType !== "application/json") { + console.log( + `[REGULAR PASTE] Overriding content type for encrypted paste from ${contentType} to application/json`, + ); + contentType = "application/json"; } - + // Check if this is markdown content that should be rendered as HTML - const isMarkdown = contentType === 'text/markdown' || - contentType === 'text/x-markdown' || - filename.endsWith('.md') || - filename.endsWith('.markdown'); - + const isMarkdown = + contentType === "text/markdown" || + contentType === "text/x-markdown" || + filename.endsWith(".md") || + filename.endsWith(".markdown"); + if (isMarkdown && !isEncrypted && !wantsRaw) { // Convert markdown to HTML for browser viewing const textContent = new TextDecoder().decode(content); const renderedHTML = await renderMarkdownAsHTML(textContent, id, filename); - + return new Response(renderedHTML, { headers: { - 'Content-Type': 'text/html; charset=utf-8', - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', - 'Pragma': 'no-cache', - 'Expires': '0', - 'X-Original-Content-Type': contentType, - 'X-Rendered-Markdown': 'true', + "Content-Type": "text/html; charset=utf-8", + "Access-Control-Allow-Origin": "*", + "Cache-Control": + "no-store, no-cache, must-revalidate, proxy-revalidate", + Pragma: "no-cache", + Expires: "0", + "X-Original-Content-Type": contentType, + "X-Rendered-Markdown": "true", }, }); } - + // Return the paste content with robust caching headers return new Response(content, { headers: { - 'Content-Type': contentType, - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', - 'Pragma': 'no-cache', - 'Expires': '0', + "Content-Type": contentType, + "Access-Control-Allow-Origin": "*", + "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate", + Pragma: "no-cache", + Expires: "0", // Add a header to indicate if the paste is encrypted - 'X-Encrypted': isEncrypted ? 'true' : 'false', + "X-Encrypted": isEncrypted ? "true" : "false", }, }); } @@ -842,24 +926,37 @@ async function handleGet(id: string, env: Env, ctx: ExecutionContext, isEncrypte * @param isOneTime Whether this is a one-time paste * @returns Complete HTML page with rendered markdown */ -async function renderMarkdownAsHTML(markdownContent: string, pasteId: string, filename: string = '', isOneTime: boolean = false): Promise { +async function renderMarkdownAsHTML( + markdownContent: string, + pasteId: string, + filename: string = "", + isOneTime: boolean = false, +): Promise { // Create a custom renderer for marked const renderer = new marked.Renderer(); let codeBlockId = 0; - + // Track if we have any mermaid diagrams let hasMermaidDiagrams = false; let mermaidBlockId = 0; - + // Override code block rendering to add syntax highlighting - renderer.code = function({ text, lang, escaped }: { text: string; lang?: string; escaped?: boolean }): string { + renderer.code = function ({ + text, + lang, + escaped, + }: { + text: string; + lang?: string; + escaped?: boolean; + }): string { const blockId = `code-block-${++codeBlockId}`; - + // Check if this is a mermaid diagram - if (lang === 'mermaid') { + if (lang === "mermaid") { hasMermaidDiagrams = true; const mermaidId = `mermaid-${++mermaidBlockId}`; - + // Return a special wrapper for mermaid diagrams with toggle functionality return `
@@ -891,11 +988,11 @@ async function renderMarkdownAsHTML(markdownContent: string, pasteId: string, fi
`; } - + // Regular code block handling let highlighted: string; - let detectedLanguage = lang || 'plaintext'; - + let detectedLanguage = lang || "plaintext"; + try { if (lang && hljs.getLanguage(lang)) { // Use specified language if it's supported @@ -904,14 +1001,14 @@ async function renderMarkdownAsHTML(markdownContent: string, pasteId: string, fi // Auto-detect language const result = hljs.highlightAuto(text); highlighted = result.value; - detectedLanguage = result.language || 'plaintext'; + detectedLanguage = result.language || "plaintext"; } } catch (err) { // Fallback to plain text if highlighting fails highlighted = escapeHtml(text); - detectedLanguage = 'plaintext'; + detectedLanguage = "plaintext"; } - + // Return the highlighted code with a wrapper for the copy button return `
@@ -927,31 +1024,31 @@ async function renderMarkdownAsHTML(markdownContent: string, pasteId: string, fi
${highlighted}
`; }; - + // Configure marked with our custom renderer marked.setOptions({ breaks: true, gfm: true, renderer: renderer, }); - + // Parse the markdown to HTML const htmlContent = await marked.parse(markdownContent); - + // Generate a title from the filename or first heading - let title = filename || 'Markdown Document'; - if (title.endsWith('.md')) { + let title = filename || "Markdown Document"; + if (title.endsWith(".md")) { title = title.slice(0, -3); - } else if (title.endsWith('.markdown')) { + } else if (title.endsWith(".markdown")) { title = title.slice(0, -9); } - + // Try to extract the first heading from the content as title const headingMatch = markdownContent.match(/^#\s+(.+)$/m); if (headingMatch) { title = headingMatch[1]; } - + // Generate the complete HTML page return ` @@ -1483,9 +1580,9 @@ async function renderMarkdownAsHTML(markdownContent: string, pasteId: string, fi DedPaste - Markdown Viewer
- ${filename ? `📄 ${escapeHtml(filename)}` : ''} + ${filename ? `📄 ${escapeHtml(filename)}` : ""} ID: ${escapeHtml(pasteId)} - ${isOneTime ? '⚠️ One-Time Paste' : ''} + ${isOneTime ? '⚠️ One-Time Paste' : ""}
@@ -1799,4 +1896,4 @@ function escapeHtml(unsafe: string): string { .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); -} \ No newline at end of file +} diff --git a/src/muiStyles.ts b/src/muiStyles.ts index f3ce3c1..d19d91d 100644 --- a/src/muiStyles.ts +++ b/src/muiStyles.ts @@ -1220,4 +1220,4 @@ curl -X POST https://paste.d3d.dev/api/paste \\ -`; \ No newline at end of file +`; diff --git a/src/types/index.ts b/src/types/index.ts index 8a56b87..e121ed9 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,7 +2,7 @@ // Key types export interface KeyInfo { - type: 'self' | 'friend' | 'pgp' | 'keybase' | 'gpg'; + type: "self" | "friend" | "pgp" | "keybase" | "gpg"; name: string; path: string | { public: string; private: string }; public?: string; @@ -14,7 +14,7 @@ export interface KeyInfo { lastUsed?: string | Date | null; addedDate?: string; id?: string; - source?: 'self' | 'friend' | 'pgp' | 'keybase' | 'gpg'; + source?: "self" | "friend" | "pgp" | "keybase" | "gpg"; expires?: string | Date; trust?: string; uids?: Array<{ uid?: string; trust?: string }>; @@ -39,7 +39,7 @@ export interface EncryptionResult { } export interface RecipientInfo { - type: 'self' | 'friend' | 'pgp' | 'keybase'; + type: "self" | "friend" | "pgp" | "keybase"; name: string; fingerprint: string; username?: string; @@ -125,8 +125,8 @@ export interface InteractiveChoice { } export interface KeyManagementAction { - action: 'list' | 'add' | 'remove' | 'export' | 'generate' | 'import'; - keyType?: 'friend' | 'pgp' | 'keybase'; + action: "list" | "add" | "remove" | "export" | "generate" | "import"; + keyType?: "friend" | "pgp" | "keybase"; keyName?: string; keyData?: string; } @@ -136,45 +136,45 @@ export class DedPasteError extends Error { constructor( message: string, public code?: string, - public statusCode?: number + public statusCode?: number, ) { super(message); - this.name = 'DedPasteError'; + this.name = "DedPasteError"; } } export class ValidationError extends DedPasteError { constructor(message: string) { - super(message, 'VALIDATION_ERROR', 400); - this.name = 'ValidationError'; + super(message, "VALIDATION_ERROR", 400); + this.name = "ValidationError"; } } export class AuthenticationError extends DedPasteError { constructor(message: string) { - super(message, 'AUTHENTICATION_ERROR', 401); - this.name = 'AuthenticationError'; + super(message, "AUTHENTICATION_ERROR", 401); + this.name = "AuthenticationError"; } } export class EncryptionError extends DedPasteError { constructor(message: string) { - super(message, 'ENCRYPTION_ERROR', 500); - this.name = 'EncryptionError'; + super(message, "ENCRYPTION_ERROR", 500); + this.name = "EncryptionError"; } } export class StorageError extends DedPasteError { constructor(message: string) { - super(message, 'STORAGE_ERROR', 500); - this.name = 'StorageError'; + super(message, "STORAGE_ERROR", 500); + this.name = "StorageError"; } } export class RateLimitError extends DedPasteError { constructor(message: string) { - super(message, 'RATE_LIMIT_ERROR', 429); - this.name = 'RateLimitError'; + super(message, "RATE_LIMIT_ERROR", 429); + this.name = "RateLimitError"; } } @@ -192,7 +192,7 @@ export interface DedPasteConfig { keyDir: string; defaultExpiry?: string; defaultEncryption?: boolean; - theme?: 'dark' | 'light' | 'auto'; + theme?: "dark" | "light" | "auto"; editor?: { theme?: string; fontSize?: number; @@ -235,7 +235,7 @@ export interface DiagnosticsOptions { } export interface DiagnosticsResult { - status: 'ok' | 'warning' | 'error'; + status: "ok" | "warning" | "error"; errors: string[]; warnings: string[]; keyDatabase: KeyDatabase | null; @@ -281,7 +281,7 @@ export interface GpgKeyInfo { uids: Array<{ uid: string; trust?: string }>; created?: Date; expires?: Date; - type: 'rsa' | 'dsa' | 'elgamal' | 'ecdh' | 'ecdsa' | 'eddsa' | string; + type: "rsa" | "dsa" | "elgamal" | "ecdh" | "ecdsa" | "eddsa" | string; trust?: string; } @@ -300,11 +300,17 @@ export interface SearchOptions { } export interface KeySearchResult extends KeyInfo { - source: 'self' | 'friend' | 'pgp' | 'keybase' | 'gpg'; + source: "self" | "friend" | "pgp" | "keybase" | "gpg"; } export interface ImportKeyOptions { - source: 'file' | 'pgp-server' | 'keybase' | 'gpg-keyring' | 'gpg-import' | 'paste'; + source: + | "file" + | "pgp-server" + | "keybase" + | "gpg-keyring" + | "gpg-import" + | "paste"; file?: string; content?: string; name?: string; @@ -338,4 +344,4 @@ export interface DecryptionOptions { password?: string; keyId?: string; private?: boolean; -} \ No newline at end of file +}