├── .gitignore ├── next-env.d.ts ├── next.config.mjs ├── package.json ├── pages └── api │ └── proxy.ts ├── README.md ├── tsconfig.json ├── src └── handle-request.ts └── pnpm-lock.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules/ 3 | .next/ 4 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const config = { 3 | async rewrites() { 4 | return [ 5 | { 6 | source: "/:_path*", 7 | destination: "/api/proxy" 8 | }, 9 | ]; 10 | }, 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "palm-proxy", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "@types/node": "latest", 6 | "@types/react": "latest", 7 | "typescript": "^5.3.3" 8 | }, 9 | "private": true, 10 | "scripts": { 11 | "next": "next", 12 | "build": "next build", 13 | "dev": "PORT=4000 next dev" 14 | }, 15 | "dependencies": { 16 | "next": "canary", 17 | "react": "latest", 18 | "react-dom": "latest" 19 | } 20 | } -------------------------------------------------------------------------------- /pages/api/proxy.ts: -------------------------------------------------------------------------------- 1 | import handleRequest from "../../src/handle-request"; 2 | 3 | export const config = { 4 | runtime: "edge", 5 | // Available languages and regions for Google AI Studio and Gemini API 6 | // https://ai.google.dev/available_regions 7 | // https://vercel.com/docs/concepts/edge-network/regions 8 | regions: [ 9 | "cle1", 10 | "iad1", 11 | "pdx1", 12 | "sfo1", 13 | "sin1", 14 | "syd1", 15 | "hnd1", 16 | "kix1", 17 | ], 18 | }; 19 | 20 | export default handleRequest; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # palm-proxy 3 | Google PaLM API proxy on Vercel Edge 4 | 5 | 6 | > [!CAUTION] 7 | > This project may be stopped from deployment or account banned by Vercel. 8 | 9 | > [!TIP] 10 | > PaLM Proxy On Netlify is Available Now. 11 | > https://simonmy.com/posts/使用netlify反向代理google-palm-api.html 12 | 13 | ## Deploy 14 | 15 | ### Deploy With Vercel 16 | 17 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fantergone%2Fpalm-proxy) 18 | 19 | 20 | ## Discussion 21 | 22 | Please Visit Simon's Blog. https://simonmy.com/posts/使用vercel反向代理google-palm-api.html 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "lib": [ 5 | "es2021" 6 | ], 7 | "jsx": "preserve", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "resolveJsonModule": true, 11 | "allowJs": true, 12 | "checkJs": false, 13 | "noEmit": true, 14 | "isolatedModules": true, 15 | "allowSyntheticDefaultImports": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "strict": true, 18 | "skipLibCheck": true, 19 | "incremental": true, 20 | "esModuleInterop": true 21 | }, 22 | "include": [ 23 | "next-env.d.ts", 24 | "**/*.ts", 25 | "**/*.tsx" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } -------------------------------------------------------------------------------- /src/handle-request.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest } from "next/server"; 2 | 3 | const pickHeaders = (headers: Headers, keys: (string | RegExp)[]): Headers => { 4 | const picked = new Headers(); 5 | for (const key of headers.keys()) { 6 | if (keys.some((k) => (typeof k === "string" ? k === key : k.test(key)))) { 7 | const value = headers.get(key); 8 | if (typeof value === "string") { 9 | picked.set(key, value); 10 | } 11 | } 12 | } 13 | return picked; 14 | }; 15 | 16 | const CORS_HEADERS: Record = { 17 | "access-control-allow-origin": "*", 18 | "access-control-allow-methods": "*", 19 | "access-control-allow-headers": "*", 20 | }; 21 | 22 | export default async function handleRequest(request: NextRequest & { nextUrl?: URL }) { 23 | if (request.method === "OPTIONS") { 24 | return new Response(null, { 25 | headers: CORS_HEADERS, 26 | }); 27 | } 28 | 29 | const { pathname, searchParams } = request.nextUrl ? request.nextUrl : new URL(request.url); 30 | 31 | if(pathname === "/") { 32 | let blank_html = ` 33 | 34 | 35 | 36 | 37 | Google PaLM API proxy on Vercel Edge 38 | 39 | 40 |

Google PaLM API proxy on Vercel Edge

41 |

Tips: This project uses a reverse proxy to solve problems such as location restrictions in Google APIs.

42 |

If you have any of the following requirements, you may need the support of this project.

43 |
    44 |
  1. When you see the error message "User location is not supported for the API use" when calling the Google PaLM API
  2. 45 |
  3. You want to customize the Google PaLM API
  4. 46 |
47 |

For technical discussions, please visit https://simonmy.com/posts/使用vercel反向代理google-palm-api.html

48 | 49 | 50 | ` 51 | return new Response(blank_html, { 52 | headers: { 53 | ...CORS_HEADERS, 54 | "content-type": "text/html" 55 | }, 56 | }); 57 | } 58 | // curl \ 59 | // -H 'Content-Type: application/json' \ 60 | // -d '{ "prompt": { "text": "Write a story about a magic backpack"} }' \ 61 | // "https://generativelanguage.googleapis.com/v1beta3/models/text-bison-001:generateText?key={YOUR_KEY}" 62 | 63 | const url = new URL(pathname, "https://generativelanguage.googleapis.com"); 64 | searchParams.delete("_path"); 65 | 66 | searchParams.forEach((value, key) => { 67 | url.searchParams.append(key, value); 68 | }); 69 | 70 | const headers = pickHeaders(request.headers, ["content-type", "x-goog-api-client", "x-goog-api-key"]); 71 | 72 | const response = await fetch(url, { 73 | body: request.body, 74 | method: request.method, 75 | headers, 76 | }); 77 | 78 | const responseHeaders = { 79 | ...CORS_HEADERS, 80 | ...Object.fromEntries(response.headers) 81 | }; 82 | 83 | return new Response(response.body, { 84 | headers: responseHeaders, 85 | status: response.status 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | next: 9 | specifier: canary 10 | version: 14.0.5-canary.17(react-dom@18.2.0)(react@18.2.0) 11 | react: 12 | specifier: latest 13 | version: 18.2.0 14 | react-dom: 15 | specifier: latest 16 | version: 18.2.0(react@18.2.0) 17 | 18 | devDependencies: 19 | '@types/node': 20 | specifier: latest 21 | version: 20.10.5 22 | '@types/react': 23 | specifier: latest 24 | version: 18.2.45 25 | typescript: 26 | specifier: ^5.3.3 27 | version: 5.3.3 28 | 29 | packages: 30 | 31 | /@next/env@14.0.5-canary.17: 32 | resolution: {integrity: sha512-YdmiBGjz18ArskwnPB7U7AwTUO7KjJ1Vn64AdAuE4pQqfRzwIyfz2VL83dsB12njoskhN95XOUBstOYUv2RF4A==} 33 | dev: false 34 | 35 | /@next/swc-darwin-arm64@14.0.5-canary.17: 36 | resolution: {integrity: sha512-/sqYvmmSV4388UVGmJrOwcTeKosUPYWBcTQ+YYS7i9gVY2aPaMlZcCXEXsuwOg5Tyav/jd0URWXuh7N9fpVqbw==} 37 | engines: {node: '>= 10'} 38 | cpu: [arm64] 39 | os: [darwin] 40 | requiresBuild: true 41 | dev: false 42 | optional: true 43 | 44 | /@next/swc-darwin-x64@14.0.5-canary.17: 45 | resolution: {integrity: sha512-JjXrN7q6R1ys3hWUEtyfvjiB2hCxFeAtSq4GVkpugPQH9J4jLXgaxEcTV54ebU5Oh5Fhnazrw6iEJ3Kte9PZZQ==} 46 | engines: {node: '>= 10'} 47 | cpu: [x64] 48 | os: [darwin] 49 | requiresBuild: true 50 | dev: false 51 | optional: true 52 | 53 | /@next/swc-linux-arm64-gnu@14.0.5-canary.17: 54 | resolution: {integrity: sha512-HTF4ib0YytW8VTcGe6+dqnJt4fE505HjN9z9U1e/OSiYcMs0xd9z5a7f/VVdez3tCt7JYbmdt5aSRebyghk/1A==} 55 | engines: {node: '>= 10'} 56 | cpu: [arm64] 57 | os: [linux] 58 | requiresBuild: true 59 | dev: false 60 | optional: true 61 | 62 | /@next/swc-linux-arm64-musl@14.0.5-canary.17: 63 | resolution: {integrity: sha512-KPiZw8nvlU94MJtlXMyN5tNPJkJbRy0obHwm69LevAg/AkS7V5RJ/p/4UESPzFJ6qRfLa2kuT6X9xQPwHleGzQ==} 64 | engines: {node: '>= 10'} 65 | cpu: [arm64] 66 | os: [linux] 67 | requiresBuild: true 68 | dev: false 69 | optional: true 70 | 71 | /@next/swc-linux-x64-gnu@14.0.5-canary.17: 72 | resolution: {integrity: sha512-B0sw8r+Tg/C9uPYFNiEdGnNIOYZgnb2hnLZrsYrcho8IN+ZpMqckaJg9CmJd/y7ABEGy2IeuVIyG3xUv/RxrMg==} 73 | engines: {node: '>= 10'} 74 | cpu: [x64] 75 | os: [linux] 76 | requiresBuild: true 77 | dev: false 78 | optional: true 79 | 80 | /@next/swc-linux-x64-musl@14.0.5-canary.17: 81 | resolution: {integrity: sha512-Nj+xxWoe9weA8Q9+tJFFARDT7fHg+Ep2PPEo+Wyh/o74K2VKfoChAm/1VwT5QcDKOmK+2zA5L1yA+oaRHayDnQ==} 82 | engines: {node: '>= 10'} 83 | cpu: [x64] 84 | os: [linux] 85 | requiresBuild: true 86 | dev: false 87 | optional: true 88 | 89 | /@next/swc-win32-arm64-msvc@14.0.5-canary.17: 90 | resolution: {integrity: sha512-DiTRhyWIIRnkllHbCcjC8gz8igtu7r9/XmnQItgCTHJLpudyQewJ+kq64NKqPzbpO07kN8D6Q+cdOE279z24UQ==} 91 | engines: {node: '>= 10'} 92 | cpu: [arm64] 93 | os: [win32] 94 | requiresBuild: true 95 | dev: false 96 | optional: true 97 | 98 | /@next/swc-win32-ia32-msvc@14.0.5-canary.17: 99 | resolution: {integrity: sha512-cfrb2fvzIrYFQMj10xssDD5iRULG3S6EjyqKWUjNChXqqj3xSQoKx4LMOqEQTl0IBVJjHhmDny5xZLwO6F6+aQ==} 100 | engines: {node: '>= 10'} 101 | cpu: [ia32] 102 | os: [win32] 103 | requiresBuild: true 104 | dev: false 105 | optional: true 106 | 107 | /@next/swc-win32-x64-msvc@14.0.5-canary.17: 108 | resolution: {integrity: sha512-Vc1djW4hEpoc9G63ekNzpFoDq0Zv9XKY3YMyUUP5aKFQgpe72T2GXCSFlVIcTgwoVKiTuDl9wyxEeYpjbMxbWw==} 109 | engines: {node: '>= 10'} 110 | cpu: [x64] 111 | os: [win32] 112 | requiresBuild: true 113 | dev: false 114 | optional: true 115 | 116 | /@swc/helpers@0.5.2: 117 | resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} 118 | dependencies: 119 | tslib: 2.6.2 120 | dev: false 121 | 122 | /@types/node@20.10.5: 123 | resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} 124 | dependencies: 125 | undici-types: 5.26.5 126 | dev: true 127 | 128 | /@types/prop-types@15.7.11: 129 | resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} 130 | dev: true 131 | 132 | /@types/react@18.2.45: 133 | resolution: {integrity: sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==} 134 | dependencies: 135 | '@types/prop-types': 15.7.11 136 | '@types/scheduler': 0.16.8 137 | csstype: 3.1.3 138 | dev: true 139 | 140 | /@types/scheduler@0.16.8: 141 | resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} 142 | dev: true 143 | 144 | /busboy@1.6.0: 145 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 146 | engines: {node: '>=10.16.0'} 147 | dependencies: 148 | streamsearch: 1.1.0 149 | dev: false 150 | 151 | /caniuse-lite@1.0.30001570: 152 | resolution: {integrity: sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==} 153 | dev: false 154 | 155 | /client-only@0.0.1: 156 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 157 | dev: false 158 | 159 | /csstype@3.1.3: 160 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 161 | dev: true 162 | 163 | /glob-to-regexp@0.4.1: 164 | resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} 165 | dev: false 166 | 167 | /graceful-fs@4.2.11: 168 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 169 | dev: false 170 | 171 | /js-tokens@4.0.0: 172 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 173 | dev: false 174 | 175 | /loose-envify@1.4.0: 176 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 177 | hasBin: true 178 | dependencies: 179 | js-tokens: 4.0.0 180 | dev: false 181 | 182 | /nanoid@3.3.7: 183 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 184 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 185 | hasBin: true 186 | dev: false 187 | 188 | /next@14.0.5-canary.17(react-dom@18.2.0)(react@18.2.0): 189 | resolution: {integrity: sha512-qetVXiLdRjbfvAbZW8MXlY/R0N2BvhkHhNOB2mGmAdOtp4YWDqM1XlYdBwoDhv1LO9sA7WGREczZIp8qyBQxVg==} 190 | engines: {node: '>=18.17.0'} 191 | hasBin: true 192 | peerDependencies: 193 | '@opentelemetry/api': ^1.1.0 194 | react: ^18.2.0 195 | react-dom: ^18.2.0 196 | sass: ^1.3.0 197 | peerDependenciesMeta: 198 | '@opentelemetry/api': 199 | optional: true 200 | sass: 201 | optional: true 202 | dependencies: 203 | '@next/env': 14.0.5-canary.17 204 | '@swc/helpers': 0.5.2 205 | busboy: 1.6.0 206 | caniuse-lite: 1.0.30001570 207 | graceful-fs: 4.2.11 208 | postcss: 8.4.31 209 | react: 18.2.0 210 | react-dom: 18.2.0(react@18.2.0) 211 | styled-jsx: 5.1.1(react@18.2.0) 212 | watchpack: 2.4.0 213 | optionalDependencies: 214 | '@next/swc-darwin-arm64': 14.0.5-canary.17 215 | '@next/swc-darwin-x64': 14.0.5-canary.17 216 | '@next/swc-linux-arm64-gnu': 14.0.5-canary.17 217 | '@next/swc-linux-arm64-musl': 14.0.5-canary.17 218 | '@next/swc-linux-x64-gnu': 14.0.5-canary.17 219 | '@next/swc-linux-x64-musl': 14.0.5-canary.17 220 | '@next/swc-win32-arm64-msvc': 14.0.5-canary.17 221 | '@next/swc-win32-ia32-msvc': 14.0.5-canary.17 222 | '@next/swc-win32-x64-msvc': 14.0.5-canary.17 223 | transitivePeerDependencies: 224 | - '@babel/core' 225 | - babel-plugin-macros 226 | dev: false 227 | 228 | /picocolors@1.0.0: 229 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 230 | dev: false 231 | 232 | /postcss@8.4.31: 233 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 234 | engines: {node: ^10 || ^12 || >=14} 235 | dependencies: 236 | nanoid: 3.3.7 237 | picocolors: 1.0.0 238 | source-map-js: 1.0.2 239 | dev: false 240 | 241 | /react-dom@18.2.0(react@18.2.0): 242 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 243 | peerDependencies: 244 | react: ^18.2.0 245 | dependencies: 246 | loose-envify: 1.4.0 247 | react: 18.2.0 248 | scheduler: 0.23.0 249 | dev: false 250 | 251 | /react@18.2.0: 252 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 253 | engines: {node: '>=0.10.0'} 254 | dependencies: 255 | loose-envify: 1.4.0 256 | dev: false 257 | 258 | /scheduler@0.23.0: 259 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 260 | dependencies: 261 | loose-envify: 1.4.0 262 | dev: false 263 | 264 | /source-map-js@1.0.2: 265 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 266 | engines: {node: '>=0.10.0'} 267 | dev: false 268 | 269 | /streamsearch@1.1.0: 270 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 271 | engines: {node: '>=10.0.0'} 272 | dev: false 273 | 274 | /styled-jsx@5.1.1(react@18.2.0): 275 | resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} 276 | engines: {node: '>= 12.0.0'} 277 | peerDependencies: 278 | '@babel/core': '*' 279 | babel-plugin-macros: '*' 280 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' 281 | peerDependenciesMeta: 282 | '@babel/core': 283 | optional: true 284 | babel-plugin-macros: 285 | optional: true 286 | dependencies: 287 | client-only: 0.0.1 288 | react: 18.2.0 289 | dev: false 290 | 291 | /tslib@2.6.2: 292 | resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} 293 | dev: false 294 | 295 | /typescript@5.3.3: 296 | resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} 297 | engines: {node: '>=14.17'} 298 | hasBin: true 299 | dev: true 300 | 301 | /undici-types@5.26.5: 302 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 303 | dev: true 304 | 305 | /watchpack@2.4.0: 306 | resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} 307 | engines: {node: '>=10.13.0'} 308 | dependencies: 309 | glob-to-regexp: 0.4.1 310 | graceful-fs: 4.2.11 311 | dev: false 312 | --------------------------------------------------------------------------------