From 0ef04e0bbf40f6a638d2956058f07be815ea6aef Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 12 Jun 2026 12:30:58 -0700 Subject: [PATCH 1/5] replace glob-slasher with glob + a utility --- package-lock.json | 370 ++++++++++++++++++------------ package.json | 2 +- src/middleware/headers.js | 2 +- src/middleware/redirects.js | 2 +- src/middleware/rewrites.ts | 2 +- src/utils/pathutils.ts | 35 +++ test/unit/utils/pathutils.spec.ts | 55 +++++ 7 files changed, 320 insertions(+), 148 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74076223..cb00f0af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "compression": "^1.7.0", "connect": "^3.7.0", "destroy": "^1.0.4", - "glob-slasher": "^1.0.1", + "glob": "^13.0.6", "is-url": "^1.2.2", "join-path": "^1.1.1", "mime-types": "^2.1.35", @@ -1877,47 +1877,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/cacache/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.5.tgz", - "integrity": "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "minimatch": "^10.2.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", @@ -1928,22 +1887,6 @@ "node": "20 || >=22" } }, - "node_modules/cacache/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/p-map": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", @@ -1957,23 +1900,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cacache/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -3244,7 +3170,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/function-bind": { "version": "1.1.2", @@ -3332,20 +3259,17 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3363,43 +3287,65 @@ "node": ">=10.13.0" } }, - "node_modules/glob-slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/glob-slash/-/glob-slash-1.0.0.tgz", - "integrity": "sha512-ZwFh34WZhZX28ntCMAP1mwyAJkn8+Omagvt/GvA+JQM/qgT0+MR2NPF3vhvgdshfdvDyGZXs8fPXW84K32Wjuw==" - }, - "node_modules/glob-slasher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glob-slasher/-/glob-slasher-1.0.1.tgz", - "integrity": "sha512-5MUzqFiycIKLMD1B0dYOE4hGgLLUZUNGGYO4BExdwT32wUwW3DBOE7lMQars7vB1q43Fb3Tyt+HmgLKsJhDYdg==", - "dependencies": { - "glob-slash": "^1.0.0", - "lodash.isobject": "^2.4.1", - "toxic": "^1.0.0" + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "dev": true, + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/global-dirs": { @@ -3665,7 +3611,9 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3925,6 +3873,52 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4150,17 +4144,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, - "node_modules/lodash._objecttypes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", - "integrity": "sha512-XpqGh1e7hhkOzftBfWE7zt+Yn9mVHFkDhicVttvKLsoCMLVVL+xTQjfjB4X4vtznauxv0QZ5ZAeqjvat0dh62Q==" - }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -4173,14 +4156,6 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, - "node_modules/lodash.isobject": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", - "integrity": "sha512-sTebg2a1PoicYEZXD5PBdQcTlIJ6hUslrlWr7iV0O7n+i4596s2NQ9I5CaZ5FbXSfya/9WQsrYLANUJv9paYVA==", - "dependencies": { - "lodash._objecttypes": "~2.4.1" - } - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4356,10 +4331,10 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "devOptional": true, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -4888,6 +4863,17 @@ "node": ">=18" } }, + "node_modules/nyc/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -4942,6 +4928,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4954,6 +4962,19 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -5267,6 +5288,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6108,6 +6130,52 @@ "node": ">=8" } }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/spawn-wrap/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6402,6 +6470,28 @@ "concat-map": "0.0.1" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -6450,14 +6540,6 @@ } } }, - "node_modules/toxic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toxic/-/toxic-1.0.1.tgz", - "integrity": "sha512-WI3rIGdcaKULYg7KVoB0zcjikqvcYYvcuT6D89bFPz2rVR0Rl0PK6x8/X62rtdLtBKIE985NzVf/auTtGegIIg==", - "dependencies": { - "lodash": "^4.17.10" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/package.json b/package.json index 12a110ff..a5e10f44 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "compression": "^1.7.0", "connect": "^3.7.0", "destroy": "^1.0.4", - "glob-slasher": "^1.0.1", + "glob": "^13.0.6", "is-url": "^1.2.2", "join-path": "^1.1.1", "mime-types": "^2.1.35", diff --git a/src/middleware/headers.js b/src/middleware/headers.js index cf36ed3f..cf552022 100644 --- a/src/middleware/headers.js +++ b/src/middleware/headers.js @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const slasher = require("glob-slasher"); +const { slasher } = require("../utils/pathutils"); const urlParser = require("url"); const onHeaders = require("on-headers"); const patterns = require("../utils/patterns"); diff --git a/src/middleware/redirects.js b/src/middleware/redirects.js index d1fb25d2..abbdde17 100644 --- a/src/middleware/redirects.js +++ b/src/middleware/redirects.js @@ -23,7 +23,7 @@ const isUrl = require("is-url"); const patterns = require("../utils/patterns"); const pathToRegexp = require("path-to-regexp"); -const slasher = require("glob-slasher"); +const { slasher } = require("../utils/pathutils"); function formatExternalUrl(u) { const cleaned = u diff --git a/src/middleware/rewrites.ts b/src/middleware/rewrites.ts index 8f17da7b..6d1b5bce 100644 --- a/src/middleware/rewrites.ts +++ b/src/middleware/rewrites.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const slasher = require("glob-slasher"); +import { slasher } from "../utils/pathutils"; import * as urlParser from "url"; import { NextFunction } from "connect"; import { IncomingMessage, ServerResponse } from "http"; diff --git a/src/utils/pathutils.ts b/src/utils/pathutils.ts index adba033f..2be8698f 100644 --- a/src/utils/pathutils.ts +++ b/src/utils/pathutils.ts @@ -83,3 +83,38 @@ export function removeTrailingString(string: string, rm: string): string { } return string.slice(0, string.lastIndexOf(rm)); } + +/** + * Ensures a glob pattern or path starts with a slash, converts backslashes + * to forward slashes, and recursively handles arrays and objects. + * Mimics `glob-slasher` but is more robust for glob matching libraries. + * @param value The value to slash (string, array, object). + * @returns The slashed value. + */ +export function slasher(value: any): any { + if (typeof value === "string") { + let isNegated = false; + let val = value; + if (val.startsWith("!")) { + isNegated = true; + val = val.substring(1); + } + if (!val.startsWith("/")) { + val = "/" + val; + } + val = val.replace(/\\/g, "/").replace(/\/+/g, "/"); + return isNegated ? "!" + val : val; + } + if (Array.isArray(value)) { + return value.map(slasher); + } + if (value !== null && typeof value === "object") { + const result: any = {}; + for (const key of Object.keys(value)) { + const newKey = slasher(key); + result[newKey] = slasher(value[key]); + } + return result; + } + return value; +} diff --git a/test/unit/utils/pathutils.spec.ts b/test/unit/utils/pathutils.spec.ts index 7001b8eb..463b1ac0 100644 --- a/test/unit/utils/pathutils.spec.ts +++ b/test/unit/utils/pathutils.spec.ts @@ -67,4 +67,59 @@ describe("pathutils", () => { ); }); }); + + describe("slasher", () => { + it("should prepend a slash if not present", () => { + expect(pathutils.slasher("foo/bar")).to.equal("/foo/bar"); + expect(pathutils.slasher("foo\\bar")).to.equal("/foo/bar"); + }); + + it("should not prepend a slash if already present", () => { + expect(pathutils.slasher("/foo/bar")).to.equal("/foo/bar"); + }); + + it("should handle negated glob patterns", () => { + expect(pathutils.slasher("!foo/bar")).to.equal("!/foo/bar"); + expect(pathutils.slasher("!/foo/bar")).to.equal("!/foo/bar"); + }); + + it("should normalize multiple slashes", () => { + expect(pathutils.slasher("foo//bar")).to.equal("/foo/bar"); + expect(pathutils.slasher("!foo///bar")).to.equal("!/foo/bar"); + }); + + it("should convert backslashes to forward slashes", () => { + expect(pathutils.slasher("foo\\bar\\baz")).to.equal("/foo/bar/baz"); + expect(pathutils.slasher("!foo\\bar")).to.equal("!/foo/bar"); + }); + + it("should recursively process arrays", () => { + expect( + pathutils.slasher(["foo/bar", "!baz/qux", "/already/slashed"]), + ).to.deep.equal(["/foo/bar", "!/baz/qux", "/already/slashed"]); + }); + + it("should recursively process objects", () => { + const input = { + "key1/path": "val1/path", + "key2\\path": { + "nested/key": "nested/value", + }, + }; + const expected = { + "/key1/path": "/val1/path", + "/key2/path": { + "/nested/key": "/nested/value", + }, + }; + expect(pathutils.slasher(input)).to.deep.equal(expected); + }); + + it("should leave non-string, non-array, non-object values as-is", () => { + expect(pathutils.slasher(null)).to.equal(null); + expect(pathutils.slasher(undefined)).to.equal(undefined); + expect(pathutils.slasher(123)).to.equal(123); + expect(pathutils.slasher(true)).to.equal(true); + }); + }); }); From 028567980f87292db8063c7f501b20836e6f7cb6 Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 12 Jun 2026 12:36:18 -0700 Subject: [PATCH 2/5] chore: remove unused glob dependency --- package-lock.json | 8 +++++++- package.json | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb00f0af..409e1a64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "compression": "^1.7.0", "connect": "^3.7.0", "destroy": "^1.0.4", - "glob": "^13.0.6", "is-url": "^1.2.2", "join-path": "^1.1.1", "mime-types": "^2.1.35", @@ -3263,6 +3262,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", + "optional": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", @@ -3292,6 +3292,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", + "optional": true, "engines": { "node": "18 || 20 || >=22" } @@ -3301,6 +3302,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", + "optional": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -3313,6 +3315,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", + "optional": true, "engines": { "node": "20 || >=22" } @@ -3322,6 +3325,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", + "optional": true, "dependencies": { "brace-expansion": "^5.0.5" }, @@ -3337,6 +3341,7 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", + "optional": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -4334,6 +4339,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "devOptional": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" diff --git a/package.json b/package.json index a5e10f44..da9ffc23 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "compression": "^1.7.0", "connect": "^3.7.0", "destroy": "^1.0.4", - "glob": "^13.0.6", "is-url": "^1.2.2", "join-path": "^1.1.1", "mime-types": "^2.1.35", From 8f035e85c8dfe21f081d7c4b7d87e44cbfc4c5b9 Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 12 Jun 2026 12:58:52 -0700 Subject: [PATCH 3/5] docs: add changelog entry for glob-slasher replacement --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 8b137891..ede47134 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1 +1 @@ - +* Replaces obsolete glob-slasher dependency with a local path/glob normalization utility. From 29c68922eafb1c312c8ba3ab9e462a6c478d544b Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 12 Jun 2026 13:09:12 -0700 Subject: [PATCH 4/5] refactor: only slash values and not keys in slasher --- src/utils/pathutils.ts | 3 +-- test/unit/utils/pathutils.spec.ts | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/utils/pathutils.ts b/src/utils/pathutils.ts index 2be8698f..b2508c38 100644 --- a/src/utils/pathutils.ts +++ b/src/utils/pathutils.ts @@ -111,8 +111,7 @@ export function slasher(value: any): any { if (value !== null && typeof value === "object") { const result: any = {}; for (const key of Object.keys(value)) { - const newKey = slasher(key); - result[newKey] = slasher(value[key]); + result[key] = slasher(value[key]); } return result; } diff --git a/test/unit/utils/pathutils.spec.ts b/test/unit/utils/pathutils.spec.ts index 463b1ac0..a53b2e91 100644 --- a/test/unit/utils/pathutils.spec.ts +++ b/test/unit/utils/pathutils.spec.ts @@ -99,7 +99,7 @@ describe("pathutils", () => { ).to.deep.equal(["/foo/bar", "!/baz/qux", "/already/slashed"]); }); - it("should recursively process objects", () => { + it("should recursively process objects without slashing keys", () => { const input = { "key1/path": "val1/path", "key2\\path": { @@ -107,9 +107,9 @@ describe("pathutils", () => { }, }; const expected = { - "/key1/path": "/val1/path", - "/key2/path": { - "/nested/key": "/nested/value", + "key1/path": "/val1/path", + "key2\\path": { + "nested/key": "/nested/value", }, }; expect(pathutils.slasher(input)).to.deep.equal(expected); From 526a859e7028168c3b900f9e41e81ae950960c84 Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 12 Jun 2026 13:11:31 -0700 Subject: [PATCH 5/5] refactor: handle non-plain objects in slasher safely --- src/utils/pathutils.ts | 4 +++- test/unit/utils/pathutils.spec.ts | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/pathutils.ts b/src/utils/pathutils.ts index b2508c38..1407b9ca 100644 --- a/src/utils/pathutils.ts +++ b/src/utils/pathutils.ts @@ -19,6 +19,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { isPlainObject } from "./objectutils"; + const INDEX_FILE = "index.html"; /** @@ -108,7 +110,7 @@ export function slasher(value: any): any { if (Array.isArray(value)) { return value.map(slasher); } - if (value !== null && typeof value === "object") { + if (isPlainObject(value)) { const result: any = {}; for (const key of Object.keys(value)) { result[key] = slasher(value[key]); diff --git a/test/unit/utils/pathutils.spec.ts b/test/unit/utils/pathutils.spec.ts index a53b2e91..4f5cc890 100644 --- a/test/unit/utils/pathutils.spec.ts +++ b/test/unit/utils/pathutils.spec.ts @@ -115,6 +115,13 @@ describe("pathutils", () => { expect(pathutils.slasher(input)).to.deep.equal(expected); }); + it("should leave non-plain objects as-is", () => { + const regex = /abc/g; + const date = new Date(); + expect(pathutils.slasher(regex)).to.equal(regex); + expect(pathutils.slasher(date)).to.equal(date); + }); + it("should leave non-string, non-array, non-object values as-is", () => { expect(pathutils.slasher(null)).to.equal(null); expect(pathutils.slasher(undefined)).to.equal(undefined);