├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── handleErrors.mjs ├── main.mjs └── router │ ├── index.mjs │ ├── r2.mjs │ └── stream.mjs └── wrangler.toml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .mf/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 VOXO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Durable Stream :earth_americas: 2 | 3 | A 0 dependency messaging server that uses [Durable Objects](https://developers.cloudflare.com/workers/learning/using-durable-objects) to provide a persistent **subject** based message stream to/from clients over Websockets. 4 | 5 | ## Why? 6 | 7 | There are plenty of **clustered** messaging solutions like [Redis](https://redis.io/), [RabbitMQ](https://www.rabbitmq.com/), and [NATS](https://nats.io). But what if you want a **serverless** solution? This project is an attempt to provide just that using [Cloudflare Workers](https://workers.cloudflare.com/) that models itself after [NATS Jetstream](https://docs.nats.io/nats-concepts/jetstream) (except a more opinionated and simplified implementation). We are also testing this out as a possible MySQL query replication query stream as it provides a global uniqueness for any number of our MySQL proxy clients running the [Durable Stream Client](https://github.com/voxoco/durable-stream-client). 8 | 9 | ## How? 10 | 11 | The server is built using [Cloudflare Workers](https://workers.cloudflare.com/), [Durable Objects](https://developers.cloudflare.com/workers/learning/using-durable-objects), and [R2](https://developers.cloudflare.com/r2/). The [Durable Stream Client](https://github.com/voxoco/durable-stream-client) uses [Websocket-Node](https://github.com/theturtle32/WebSocket-Node). 12 | 13 | For every new message received by a client, the server will increment the sequence number and store the message using the storage API. The server will then broadcast the message to all connected clients (in an eventually consistent manner). When a new client comes online, by default the server will send all messages in the stream but the client can also request a specific sequence number to start from. 14 | 15 | ## What? 16 | 17 | The server acts as a message broker for clients. Any number of clients can connect to the server and the server will broadcast any messages received to all connected clients. Clients must ack messages. 18 | 19 | The server also allows the clients to send commands (e.g. `getServerInfo`, `subscribe`, `unsubscribe`, `deleteMessages`, `getState`, `putState`) and the server will respond to the client with the result of the command in a request/reply manner. 20 | 21 | The server also provides a full*ish* API for `GET`, `POST`, and `DELETE` requests for R2 (storing files). 22 | 23 | * Supports request/reply semantics 24 | * Requires clients to ack messages broadcasted to the stream subject 25 | * Supports multiple stream subjects (based on the url path) 26 | * Always aware of the current sequence number for the stream subject even if the server restarts/deploys 27 | * Prefers frequent checkpoints to keep storage costs down and prevent unnecessary message syncing when new clients come online 28 | * Simple to use 29 | * Extremely lightweight 30 | * Works with Miniflare (for local dev) 31 | 32 | ## Usage 33 | 34 | ### Local dev 35 | 36 | Create a `.env` file with the following: 37 | 38 | ```bash 39 | API_KEY= 40 | ``` 41 | 42 | ```bash 43 | npm install 44 | npm run dev 45 | ``` 46 | 47 | Run the [Durable Stream Client](https://github.com/voxoco/durable-stream-client) specifying the `host` as `ws://localhost:8787`, and `secure` as `false`. 48 | 49 | ### Deploy 50 | 51 | Before deploying, you will need a paid workers subscription. You also need to create an R2 bucket and add the bucket name to `bucket_name` in the `wrangler.toml` file. Then run: 52 | 53 | ```bash 54 | npm run deploy 55 | ``` 56 | 57 | After deploying, add an `API_KEY` environment variable to the worker. This will be used to authenticate clients. 58 | 59 | ## TODO 60 | 61 | - [ ] Do not send messages to clients that have not acked the previous message (or some other mechanism to prevent clients from getting messages out of order). This will require a `waitingOperations` queue for each client. 62 | - [ ] `waitingOperations` needs some love. It could cause a memory leak if the client never acks a message. We should probably have a max size for the queue and a max time to wait for an ack. 63 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cf-test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cf-test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "miniflare": "^2.7.1", 13 | "wrangler": "^2.0.28" 14 | } 15 | }, 16 | "node_modules/@cloudflare/kv-asset-handler": { 17 | "version": "0.2.0", 18 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", 19 | "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", 20 | "dev": true, 21 | "dependencies": { 22 | "mime": "^3.0.0" 23 | } 24 | }, 25 | "node_modules/@esbuild-plugins/node-globals-polyfill": { 26 | "version": "0.1.1", 27 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", 28 | "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", 29 | "dev": true, 30 | "peerDependencies": { 31 | "esbuild": "*" 32 | } 33 | }, 34 | "node_modules/@esbuild-plugins/node-modules-polyfill": { 35 | "version": "0.1.4", 36 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", 37 | "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", 38 | "dev": true, 39 | "dependencies": { 40 | "escape-string-regexp": "^4.0.0", 41 | "rollup-plugin-node-polyfills": "^0.2.1" 42 | }, 43 | "peerDependencies": { 44 | "esbuild": "*" 45 | } 46 | }, 47 | "node_modules/@iarna/toml": { 48 | "version": "2.2.5", 49 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 50 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 51 | "dev": true 52 | }, 53 | "node_modules/@miniflare/cache": { 54 | "version": "2.7.1", 55 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.7.1.tgz", 56 | "integrity": "sha512-QxN4yp8+cIlggbjIVP17xbSOjjJMco4coW5mXNPcTXazvqnbslwie9GDWmt4BkRvP77uwomf2CDUqEgxZC0frw==", 57 | "dev": true, 58 | "dependencies": { 59 | "@miniflare/core": "2.7.1", 60 | "@miniflare/shared": "2.7.1", 61 | "http-cache-semantics": "^4.1.0", 62 | "undici": "5.9.1" 63 | }, 64 | "engines": { 65 | "node": ">=16.13" 66 | } 67 | }, 68 | "node_modules/@miniflare/cli-parser": { 69 | "version": "2.7.1", 70 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.7.1.tgz", 71 | "integrity": "sha512-kuY6sWClFBQoc22g7P7gR3fv5dXDI8ezvPvNX6tHXPLiPxiYCoz8XTRUqG5CW12zTxrI3yPjEaTQoFlHzdnQkg==", 72 | "dev": true, 73 | "dependencies": { 74 | "@miniflare/shared": "2.7.1", 75 | "kleur": "^4.1.4" 76 | }, 77 | "engines": { 78 | "node": ">=16.13" 79 | } 80 | }, 81 | "node_modules/@miniflare/core": { 82 | "version": "2.7.1", 83 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.7.1.tgz", 84 | "integrity": "sha512-Pdq5+FPSg0L0/eUOKrEfGFowcmbcEXKCIJa8iYz1iA35koSytgTN+6zeuuGPGVXQbGGEPhNugWlOz4u70FJ1GA==", 85 | "dev": true, 86 | "dependencies": { 87 | "@iarna/toml": "^2.2.5", 88 | "@miniflare/shared": "2.7.1", 89 | "@miniflare/watcher": "2.7.1", 90 | "busboy": "^1.6.0", 91 | "dotenv": "^10.0.0", 92 | "kleur": "^4.1.4", 93 | "set-cookie-parser": "^2.4.8", 94 | "undici": "5.9.1", 95 | "urlpattern-polyfill": "^4.0.3" 96 | }, 97 | "engines": { 98 | "node": ">=16.13" 99 | } 100 | }, 101 | "node_modules/@miniflare/durable-objects": { 102 | "version": "2.7.1", 103 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.7.1.tgz", 104 | "integrity": "sha512-bzTzhu9KgtBZ3itR/u/izBHBzQnxhfOt1IQcJNCM/TBwSf8wr6ztDdsTDFE0j9/oQYj4umbGynzZvYYUm/SniQ==", 105 | "dev": true, 106 | "dependencies": { 107 | "@miniflare/core": "2.7.1", 108 | "@miniflare/shared": "2.7.1", 109 | "@miniflare/storage-memory": "2.7.1", 110 | "undici": "5.9.1" 111 | }, 112 | "engines": { 113 | "node": ">=16.13" 114 | } 115 | }, 116 | "node_modules/@miniflare/html-rewriter": { 117 | "version": "2.7.1", 118 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.7.1.tgz", 119 | "integrity": "sha512-7088TlpQBXdKX1OPOL+34xKSF5IjiHyjggM7HizJG14IIw1kSiJYojqaOi5f/DxstTUJJCOIxHn3zKf6QSpukA==", 120 | "dev": true, 121 | "dependencies": { 122 | "@miniflare/core": "2.7.1", 123 | "@miniflare/shared": "2.7.1", 124 | "html-rewriter-wasm": "^0.4.1", 125 | "undici": "5.9.1" 126 | }, 127 | "engines": { 128 | "node": ">=16.13" 129 | } 130 | }, 131 | "node_modules/@miniflare/http-server": { 132 | "version": "2.7.1", 133 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.7.1.tgz", 134 | "integrity": "sha512-fcLrEVxtwMhj3qO5Wg5844s6WNTiixRjGEV/Top2TjP3CM6DtIc5l6zca4vozaTba39So627NDalLZQaCAcSBQ==", 135 | "dev": true, 136 | "dependencies": { 137 | "@miniflare/core": "2.7.1", 138 | "@miniflare/shared": "2.7.1", 139 | "@miniflare/web-sockets": "2.7.1", 140 | "kleur": "^4.1.4", 141 | "selfsigned": "^2.0.0", 142 | "undici": "5.9.1", 143 | "ws": "^8.2.2", 144 | "youch": "^2.2.2" 145 | }, 146 | "engines": { 147 | "node": ">=16.13" 148 | } 149 | }, 150 | "node_modules/@miniflare/kv": { 151 | "version": "2.7.1", 152 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.7.1.tgz", 153 | "integrity": "sha512-p3BUSgp2BK2l7GxM9wVnaXTM8/thzCzAITDbeyZLevtd8r3Vl1rE8W9Q+qrUbX454+zvHfG71O+BdtfFchgWkA==", 154 | "dev": true, 155 | "dependencies": { 156 | "@miniflare/shared": "2.7.1" 157 | }, 158 | "engines": { 159 | "node": ">=16.13" 160 | } 161 | }, 162 | "node_modules/@miniflare/r2": { 163 | "version": "2.7.1", 164 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.7.1.tgz", 165 | "integrity": "sha512-UFqU2y4Qccto4PilHEn8JpTKi+lPZ61eV0G50Nnfnwa19yDKf0Wu6rYXecLTPetln10v6pCLvRvk4O93d99A6Q==", 166 | "dev": true, 167 | "dependencies": { 168 | "@miniflare/shared": "2.7.1", 169 | "undici": "5.9.1" 170 | }, 171 | "engines": { 172 | "node": ">=16.13" 173 | } 174 | }, 175 | "node_modules/@miniflare/runner-vm": { 176 | "version": "2.7.1", 177 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.7.1.tgz", 178 | "integrity": "sha512-kcntTSq38Jk81EQbEYs1wSrcziz/KO1JD1DyyDSw1C9pDSFmhusgObDW0VxaGgEVyh92No8l5CNlTjY7kjiMHw==", 179 | "dev": true, 180 | "dependencies": { 181 | "@miniflare/shared": "2.7.1" 182 | }, 183 | "engines": { 184 | "node": ">=16.13" 185 | } 186 | }, 187 | "node_modules/@miniflare/scheduler": { 188 | "version": "2.7.1", 189 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.7.1.tgz", 190 | "integrity": "sha512-00DCtvSi0/Kamo1OLtvfG+zxAS9VqrFO8Q1Wg7yEJpJBUlnUn+oOXKT//aCpZuVBJLSf7tXxzRXJYNPpu09fwg==", 191 | "dev": true, 192 | "dependencies": { 193 | "@miniflare/core": "2.7.1", 194 | "@miniflare/shared": "2.7.1", 195 | "cron-schedule": "^3.0.4" 196 | }, 197 | "engines": { 198 | "node": ">=16.13" 199 | } 200 | }, 201 | "node_modules/@miniflare/shared": { 202 | "version": "2.7.1", 203 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.7.1.tgz", 204 | "integrity": "sha512-hQsx/mt5N/zBxJ3DyAJyGMtdT07WeuU+nYiWjkIwQOkPgH/p72Xu0tdi2kO/KQogtxeT2B+eTMVXlE0JqZOyhA==", 205 | "dev": true, 206 | "dependencies": { 207 | "kleur": "^4.1.4", 208 | "picomatch": "^2.3.1" 209 | }, 210 | "engines": { 211 | "node": ">=16.13" 212 | } 213 | }, 214 | "node_modules/@miniflare/sites": { 215 | "version": "2.7.1", 216 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.7.1.tgz", 217 | "integrity": "sha512-b5pgVx5qifb9YejBfWjh5lnphc7wTX41CvBxssmCdQCxvQ+C5LgNelccNUvIBIMC+N5Ids+Fbd+Hx8MNGjp3iw==", 218 | "dev": true, 219 | "dependencies": { 220 | "@miniflare/kv": "2.7.1", 221 | "@miniflare/shared": "2.7.1", 222 | "@miniflare/storage-file": "2.7.1" 223 | }, 224 | "engines": { 225 | "node": ">=16.13" 226 | } 227 | }, 228 | "node_modules/@miniflare/storage-file": { 229 | "version": "2.7.1", 230 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.7.1.tgz", 231 | "integrity": "sha512-6WiLGCeE1jIDJ3pp2ff1vFWCH1uf9BNWRkF3FpK7LyINzdDUlV56RtchPTBgk61oE8NYjlTqoYd4+KUvBul3/w==", 232 | "dev": true, 233 | "dependencies": { 234 | "@miniflare/shared": "2.7.1", 235 | "@miniflare/storage-memory": "2.7.1" 236 | }, 237 | "engines": { 238 | "node": ">=16.13" 239 | } 240 | }, 241 | "node_modules/@miniflare/storage-memory": { 242 | "version": "2.7.1", 243 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.7.1.tgz", 244 | "integrity": "sha512-/YD6PshGEQneLmPC/FO+TnhN2STXT4oTuPxVo81fZ+q/XKglTA8iULtcgmF025lZ8S871ZANfmBtUzlxZJmW8Q==", 245 | "dev": true, 246 | "dependencies": { 247 | "@miniflare/shared": "2.7.1" 248 | }, 249 | "engines": { 250 | "node": ">=16.13" 251 | } 252 | }, 253 | "node_modules/@miniflare/watcher": { 254 | "version": "2.7.1", 255 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.7.1.tgz", 256 | "integrity": "sha512-0P0jG2IoMIQtX2JHTABY13Yq3Fs2w5gs6f/LG/X0O9pBCN3SxeQXt0bp3ELkEHjNANQWLMUs6aohb7yZ6ZTfHg==", 257 | "dev": true, 258 | "dependencies": { 259 | "@miniflare/shared": "2.7.1" 260 | }, 261 | "engines": { 262 | "node": ">=16.13" 263 | } 264 | }, 265 | "node_modules/@miniflare/web-sockets": { 266 | "version": "2.7.1", 267 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.7.1.tgz", 268 | "integrity": "sha512-VO0BhkYDn82LTRhvK1vJA1/PA9GXMJGlkt2wYomdQFOz4Rmybau4sgVyAdKWTTYV7XexEVAVRl8BDUM97Pdxvw==", 269 | "dev": true, 270 | "dependencies": { 271 | "@miniflare/core": "2.7.1", 272 | "@miniflare/shared": "2.7.1", 273 | "undici": "5.9.1", 274 | "ws": "^8.2.2" 275 | }, 276 | "engines": { 277 | "node": ">=16.13" 278 | } 279 | }, 280 | "node_modules/@types/stack-trace": { 281 | "version": "0.0.29", 282 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 283 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 284 | "dev": true 285 | }, 286 | "node_modules/anymatch": { 287 | "version": "3.1.2", 288 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 289 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 290 | "dev": true, 291 | "dependencies": { 292 | "normalize-path": "^3.0.0", 293 | "picomatch": "^2.0.4" 294 | }, 295 | "engines": { 296 | "node": ">= 8" 297 | } 298 | }, 299 | "node_modules/binary-extensions": { 300 | "version": "2.2.0", 301 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 302 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 303 | "dev": true, 304 | "engines": { 305 | "node": ">=8" 306 | } 307 | }, 308 | "node_modules/blake3-wasm": { 309 | "version": "2.1.5", 310 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 311 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 312 | "dev": true 313 | }, 314 | "node_modules/braces": { 315 | "version": "3.0.2", 316 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 317 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 318 | "dev": true, 319 | "dependencies": { 320 | "fill-range": "^7.0.1" 321 | }, 322 | "engines": { 323 | "node": ">=8" 324 | } 325 | }, 326 | "node_modules/buffer-from": { 327 | "version": "1.1.2", 328 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 329 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 330 | "dev": true 331 | }, 332 | "node_modules/busboy": { 333 | "version": "1.6.0", 334 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 335 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 336 | "dev": true, 337 | "dependencies": { 338 | "streamsearch": "^1.1.0" 339 | }, 340 | "engines": { 341 | "node": ">=10.16.0" 342 | } 343 | }, 344 | "node_modules/chokidar": { 345 | "version": "3.5.3", 346 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 347 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 348 | "dev": true, 349 | "funding": [ 350 | { 351 | "type": "individual", 352 | "url": "https://paulmillr.com/funding/" 353 | } 354 | ], 355 | "dependencies": { 356 | "anymatch": "~3.1.2", 357 | "braces": "~3.0.2", 358 | "glob-parent": "~5.1.2", 359 | "is-binary-path": "~2.1.0", 360 | "is-glob": "~4.0.1", 361 | "normalize-path": "~3.0.0", 362 | "readdirp": "~3.6.0" 363 | }, 364 | "engines": { 365 | "node": ">= 8.10.0" 366 | }, 367 | "optionalDependencies": { 368 | "fsevents": "~2.3.2" 369 | } 370 | }, 371 | "node_modules/cookie": { 372 | "version": "0.4.2", 373 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 374 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 375 | "dev": true, 376 | "engines": { 377 | "node": ">= 0.6" 378 | } 379 | }, 380 | "node_modules/cron-schedule": { 381 | "version": "3.0.6", 382 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 383 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 384 | "dev": true 385 | }, 386 | "node_modules/dotenv": { 387 | "version": "10.0.0", 388 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 389 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 390 | "dev": true, 391 | "engines": { 392 | "node": ">=10" 393 | } 394 | }, 395 | "node_modules/esbuild": { 396 | "version": "0.14.51", 397 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz", 398 | "integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==", 399 | "dev": true, 400 | "hasInstallScript": true, 401 | "bin": { 402 | "esbuild": "bin/esbuild" 403 | }, 404 | "engines": { 405 | "node": ">=12" 406 | }, 407 | "optionalDependencies": { 408 | "esbuild-android-64": "0.14.51", 409 | "esbuild-android-arm64": "0.14.51", 410 | "esbuild-darwin-64": "0.14.51", 411 | "esbuild-darwin-arm64": "0.14.51", 412 | "esbuild-freebsd-64": "0.14.51", 413 | "esbuild-freebsd-arm64": "0.14.51", 414 | "esbuild-linux-32": "0.14.51", 415 | "esbuild-linux-64": "0.14.51", 416 | "esbuild-linux-arm": "0.14.51", 417 | "esbuild-linux-arm64": "0.14.51", 418 | "esbuild-linux-mips64le": "0.14.51", 419 | "esbuild-linux-ppc64le": "0.14.51", 420 | "esbuild-linux-riscv64": "0.14.51", 421 | "esbuild-linux-s390x": "0.14.51", 422 | "esbuild-netbsd-64": "0.14.51", 423 | "esbuild-openbsd-64": "0.14.51", 424 | "esbuild-sunos-64": "0.14.51", 425 | "esbuild-windows-32": "0.14.51", 426 | "esbuild-windows-64": "0.14.51", 427 | "esbuild-windows-arm64": "0.14.51" 428 | } 429 | }, 430 | "node_modules/esbuild-android-64": { 431 | "version": "0.14.51", 432 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz", 433 | "integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==", 434 | "cpu": [ 435 | "x64" 436 | ], 437 | "dev": true, 438 | "optional": true, 439 | "os": [ 440 | "android" 441 | ], 442 | "engines": { 443 | "node": ">=12" 444 | } 445 | }, 446 | "node_modules/esbuild-android-arm64": { 447 | "version": "0.14.51", 448 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz", 449 | "integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==", 450 | "cpu": [ 451 | "arm64" 452 | ], 453 | "dev": true, 454 | "optional": true, 455 | "os": [ 456 | "android" 457 | ], 458 | "engines": { 459 | "node": ">=12" 460 | } 461 | }, 462 | "node_modules/esbuild-darwin-64": { 463 | "version": "0.14.51", 464 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz", 465 | "integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==", 466 | "cpu": [ 467 | "x64" 468 | ], 469 | "dev": true, 470 | "optional": true, 471 | "os": [ 472 | "darwin" 473 | ], 474 | "engines": { 475 | "node": ">=12" 476 | } 477 | }, 478 | "node_modules/esbuild-darwin-arm64": { 479 | "version": "0.14.51", 480 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz", 481 | "integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==", 482 | "cpu": [ 483 | "arm64" 484 | ], 485 | "dev": true, 486 | "optional": true, 487 | "os": [ 488 | "darwin" 489 | ], 490 | "engines": { 491 | "node": ">=12" 492 | } 493 | }, 494 | "node_modules/esbuild-freebsd-64": { 495 | "version": "0.14.51", 496 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz", 497 | "integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==", 498 | "cpu": [ 499 | "x64" 500 | ], 501 | "dev": true, 502 | "optional": true, 503 | "os": [ 504 | "freebsd" 505 | ], 506 | "engines": { 507 | "node": ">=12" 508 | } 509 | }, 510 | "node_modules/esbuild-freebsd-arm64": { 511 | "version": "0.14.51", 512 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz", 513 | "integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==", 514 | "cpu": [ 515 | "arm64" 516 | ], 517 | "dev": true, 518 | "optional": true, 519 | "os": [ 520 | "freebsd" 521 | ], 522 | "engines": { 523 | "node": ">=12" 524 | } 525 | }, 526 | "node_modules/esbuild-linux-32": { 527 | "version": "0.14.51", 528 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz", 529 | "integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==", 530 | "cpu": [ 531 | "ia32" 532 | ], 533 | "dev": true, 534 | "optional": true, 535 | "os": [ 536 | "linux" 537 | ], 538 | "engines": { 539 | "node": ">=12" 540 | } 541 | }, 542 | "node_modules/esbuild-linux-64": { 543 | "version": "0.14.51", 544 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz", 545 | "integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==", 546 | "cpu": [ 547 | "x64" 548 | ], 549 | "dev": true, 550 | "optional": true, 551 | "os": [ 552 | "linux" 553 | ], 554 | "engines": { 555 | "node": ">=12" 556 | } 557 | }, 558 | "node_modules/esbuild-linux-arm": { 559 | "version": "0.14.51", 560 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz", 561 | "integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==", 562 | "cpu": [ 563 | "arm" 564 | ], 565 | "dev": true, 566 | "optional": true, 567 | "os": [ 568 | "linux" 569 | ], 570 | "engines": { 571 | "node": ">=12" 572 | } 573 | }, 574 | "node_modules/esbuild-linux-arm64": { 575 | "version": "0.14.51", 576 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz", 577 | "integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==", 578 | "cpu": [ 579 | "arm64" 580 | ], 581 | "dev": true, 582 | "optional": true, 583 | "os": [ 584 | "linux" 585 | ], 586 | "engines": { 587 | "node": ">=12" 588 | } 589 | }, 590 | "node_modules/esbuild-linux-mips64le": { 591 | "version": "0.14.51", 592 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz", 593 | "integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==", 594 | "cpu": [ 595 | "mips64el" 596 | ], 597 | "dev": true, 598 | "optional": true, 599 | "os": [ 600 | "linux" 601 | ], 602 | "engines": { 603 | "node": ">=12" 604 | } 605 | }, 606 | "node_modules/esbuild-linux-ppc64le": { 607 | "version": "0.14.51", 608 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz", 609 | "integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==", 610 | "cpu": [ 611 | "ppc64" 612 | ], 613 | "dev": true, 614 | "optional": true, 615 | "os": [ 616 | "linux" 617 | ], 618 | "engines": { 619 | "node": ">=12" 620 | } 621 | }, 622 | "node_modules/esbuild-linux-riscv64": { 623 | "version": "0.14.51", 624 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz", 625 | "integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==", 626 | "cpu": [ 627 | "riscv64" 628 | ], 629 | "dev": true, 630 | "optional": true, 631 | "os": [ 632 | "linux" 633 | ], 634 | "engines": { 635 | "node": ">=12" 636 | } 637 | }, 638 | "node_modules/esbuild-linux-s390x": { 639 | "version": "0.14.51", 640 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz", 641 | "integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==", 642 | "cpu": [ 643 | "s390x" 644 | ], 645 | "dev": true, 646 | "optional": true, 647 | "os": [ 648 | "linux" 649 | ], 650 | "engines": { 651 | "node": ">=12" 652 | } 653 | }, 654 | "node_modules/esbuild-netbsd-64": { 655 | "version": "0.14.51", 656 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz", 657 | "integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==", 658 | "cpu": [ 659 | "x64" 660 | ], 661 | "dev": true, 662 | "optional": true, 663 | "os": [ 664 | "netbsd" 665 | ], 666 | "engines": { 667 | "node": ">=12" 668 | } 669 | }, 670 | "node_modules/esbuild-openbsd-64": { 671 | "version": "0.14.51", 672 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz", 673 | "integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==", 674 | "cpu": [ 675 | "x64" 676 | ], 677 | "dev": true, 678 | "optional": true, 679 | "os": [ 680 | "openbsd" 681 | ], 682 | "engines": { 683 | "node": ">=12" 684 | } 685 | }, 686 | "node_modules/esbuild-sunos-64": { 687 | "version": "0.14.51", 688 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz", 689 | "integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==", 690 | "cpu": [ 691 | "x64" 692 | ], 693 | "dev": true, 694 | "optional": true, 695 | "os": [ 696 | "sunos" 697 | ], 698 | "engines": { 699 | "node": ">=12" 700 | } 701 | }, 702 | "node_modules/esbuild-windows-32": { 703 | "version": "0.14.51", 704 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz", 705 | "integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==", 706 | "cpu": [ 707 | "ia32" 708 | ], 709 | "dev": true, 710 | "optional": true, 711 | "os": [ 712 | "win32" 713 | ], 714 | "engines": { 715 | "node": ">=12" 716 | } 717 | }, 718 | "node_modules/esbuild-windows-64": { 719 | "version": "0.14.51", 720 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz", 721 | "integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==", 722 | "cpu": [ 723 | "x64" 724 | ], 725 | "dev": true, 726 | "optional": true, 727 | "os": [ 728 | "win32" 729 | ], 730 | "engines": { 731 | "node": ">=12" 732 | } 733 | }, 734 | "node_modules/esbuild-windows-arm64": { 735 | "version": "0.14.51", 736 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz", 737 | "integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==", 738 | "cpu": [ 739 | "arm64" 740 | ], 741 | "dev": true, 742 | "optional": true, 743 | "os": [ 744 | "win32" 745 | ], 746 | "engines": { 747 | "node": ">=12" 748 | } 749 | }, 750 | "node_modules/escape-string-regexp": { 751 | "version": "4.0.0", 752 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 753 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 754 | "dev": true, 755 | "engines": { 756 | "node": ">=10" 757 | }, 758 | "funding": { 759 | "url": "https://github.com/sponsors/sindresorhus" 760 | } 761 | }, 762 | "node_modules/estree-walker": { 763 | "version": "0.6.1", 764 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 765 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 766 | "dev": true 767 | }, 768 | "node_modules/fill-range": { 769 | "version": "7.0.1", 770 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 771 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 772 | "dev": true, 773 | "dependencies": { 774 | "to-regex-range": "^5.0.1" 775 | }, 776 | "engines": { 777 | "node": ">=8" 778 | } 779 | }, 780 | "node_modules/fsevents": { 781 | "version": "2.3.2", 782 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 783 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 784 | "dev": true, 785 | "hasInstallScript": true, 786 | "optional": true, 787 | "os": [ 788 | "darwin" 789 | ], 790 | "engines": { 791 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 792 | } 793 | }, 794 | "node_modules/glob-parent": { 795 | "version": "5.1.2", 796 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 797 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 798 | "dev": true, 799 | "dependencies": { 800 | "is-glob": "^4.0.1" 801 | }, 802 | "engines": { 803 | "node": ">= 6" 804 | } 805 | }, 806 | "node_modules/html-rewriter-wasm": { 807 | "version": "0.4.1", 808 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 809 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 810 | "dev": true 811 | }, 812 | "node_modules/http-cache-semantics": { 813 | "version": "4.1.0", 814 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 815 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", 816 | "dev": true 817 | }, 818 | "node_modules/is-binary-path": { 819 | "version": "2.1.0", 820 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 821 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 822 | "dev": true, 823 | "dependencies": { 824 | "binary-extensions": "^2.0.0" 825 | }, 826 | "engines": { 827 | "node": ">=8" 828 | } 829 | }, 830 | "node_modules/is-extglob": { 831 | "version": "2.1.1", 832 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 833 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 834 | "dev": true, 835 | "engines": { 836 | "node": ">=0.10.0" 837 | } 838 | }, 839 | "node_modules/is-glob": { 840 | "version": "4.0.3", 841 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 842 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 843 | "dev": true, 844 | "dependencies": { 845 | "is-extglob": "^2.1.1" 846 | }, 847 | "engines": { 848 | "node": ">=0.10.0" 849 | } 850 | }, 851 | "node_modules/is-number": { 852 | "version": "7.0.0", 853 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 854 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 855 | "dev": true, 856 | "engines": { 857 | "node": ">=0.12.0" 858 | } 859 | }, 860 | "node_modules/kleur": { 861 | "version": "4.1.5", 862 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 863 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 864 | "dev": true, 865 | "engines": { 866 | "node": ">=6" 867 | } 868 | }, 869 | "node_modules/magic-string": { 870 | "version": "0.25.9", 871 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", 872 | "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", 873 | "dev": true, 874 | "dependencies": { 875 | "sourcemap-codec": "^1.4.8" 876 | } 877 | }, 878 | "node_modules/mime": { 879 | "version": "3.0.0", 880 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 881 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 882 | "dev": true, 883 | "bin": { 884 | "mime": "cli.js" 885 | }, 886 | "engines": { 887 | "node": ">=10.0.0" 888 | } 889 | }, 890 | "node_modules/miniflare": { 891 | "version": "2.7.1", 892 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.7.1.tgz", 893 | "integrity": "sha512-O9kjSORazNCAGVkS0bRHhKGH1LcFOJZyBD0TchB02TalnQ3W21+QWO5PAXDGz/IATO8C8iXrPnN2XKDdDav2CA==", 894 | "dev": true, 895 | "dependencies": { 896 | "@miniflare/cache": "2.7.1", 897 | "@miniflare/cli-parser": "2.7.1", 898 | "@miniflare/core": "2.7.1", 899 | "@miniflare/durable-objects": "2.7.1", 900 | "@miniflare/html-rewriter": "2.7.1", 901 | "@miniflare/http-server": "2.7.1", 902 | "@miniflare/kv": "2.7.1", 903 | "@miniflare/r2": "2.7.1", 904 | "@miniflare/runner-vm": "2.7.1", 905 | "@miniflare/scheduler": "2.7.1", 906 | "@miniflare/shared": "2.7.1", 907 | "@miniflare/sites": "2.7.1", 908 | "@miniflare/storage-file": "2.7.1", 909 | "@miniflare/storage-memory": "2.7.1", 910 | "@miniflare/web-sockets": "2.7.1", 911 | "kleur": "^4.1.4", 912 | "semiver": "^1.1.0", 913 | "source-map-support": "^0.5.20", 914 | "undici": "5.9.1" 915 | }, 916 | "bin": { 917 | "miniflare": "bootstrap.js" 918 | }, 919 | "engines": { 920 | "node": ">=16.13" 921 | }, 922 | "peerDependencies": { 923 | "@miniflare/storage-redis": "2.7.1", 924 | "cron-schedule": "^3.0.4", 925 | "ioredis": "^4.27.9" 926 | }, 927 | "peerDependenciesMeta": { 928 | "@miniflare/storage-redis": { 929 | "optional": true 930 | }, 931 | "cron-schedule": { 932 | "optional": true 933 | }, 934 | "ioredis": { 935 | "optional": true 936 | } 937 | } 938 | }, 939 | "node_modules/mustache": { 940 | "version": "4.2.0", 941 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 942 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 943 | "dev": true, 944 | "bin": { 945 | "mustache": "bin/mustache" 946 | } 947 | }, 948 | "node_modules/nanoid": { 949 | "version": "3.3.4", 950 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 951 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 952 | "dev": true, 953 | "bin": { 954 | "nanoid": "bin/nanoid.cjs" 955 | }, 956 | "engines": { 957 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 958 | } 959 | }, 960 | "node_modules/node-forge": { 961 | "version": "1.3.1", 962 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 963 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 964 | "dev": true, 965 | "engines": { 966 | "node": ">= 6.13.0" 967 | } 968 | }, 969 | "node_modules/normalize-path": { 970 | "version": "3.0.0", 971 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 972 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 973 | "dev": true, 974 | "engines": { 975 | "node": ">=0.10.0" 976 | } 977 | }, 978 | "node_modules/path-to-regexp": { 979 | "version": "6.2.1", 980 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", 981 | "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", 982 | "dev": true 983 | }, 984 | "node_modules/picomatch": { 985 | "version": "2.3.1", 986 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 987 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 988 | "dev": true, 989 | "engines": { 990 | "node": ">=8.6" 991 | }, 992 | "funding": { 993 | "url": "https://github.com/sponsors/jonschlinkert" 994 | } 995 | }, 996 | "node_modules/readdirp": { 997 | "version": "3.6.0", 998 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 999 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1000 | "dev": true, 1001 | "dependencies": { 1002 | "picomatch": "^2.2.1" 1003 | }, 1004 | "engines": { 1005 | "node": ">=8.10.0" 1006 | } 1007 | }, 1008 | "node_modules/rollup-plugin-inject": { 1009 | "version": "3.0.2", 1010 | "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", 1011 | "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", 1012 | "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", 1013 | "dev": true, 1014 | "dependencies": { 1015 | "estree-walker": "^0.6.1", 1016 | "magic-string": "^0.25.3", 1017 | "rollup-pluginutils": "^2.8.1" 1018 | } 1019 | }, 1020 | "node_modules/rollup-plugin-node-polyfills": { 1021 | "version": "0.2.1", 1022 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", 1023 | "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", 1024 | "dev": true, 1025 | "dependencies": { 1026 | "rollup-plugin-inject": "^3.0.0" 1027 | } 1028 | }, 1029 | "node_modules/rollup-pluginutils": { 1030 | "version": "2.8.2", 1031 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 1032 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 1033 | "dev": true, 1034 | "dependencies": { 1035 | "estree-walker": "^0.6.1" 1036 | } 1037 | }, 1038 | "node_modules/selfsigned": { 1039 | "version": "2.0.1", 1040 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", 1041 | "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", 1042 | "dev": true, 1043 | "dependencies": { 1044 | "node-forge": "^1" 1045 | }, 1046 | "engines": { 1047 | "node": ">=10" 1048 | } 1049 | }, 1050 | "node_modules/semiver": { 1051 | "version": "1.1.0", 1052 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1053 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1054 | "dev": true, 1055 | "engines": { 1056 | "node": ">=6" 1057 | } 1058 | }, 1059 | "node_modules/set-cookie-parser": { 1060 | "version": "2.5.1", 1061 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 1062 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", 1063 | "dev": true 1064 | }, 1065 | "node_modules/source-map": { 1066 | "version": "0.6.1", 1067 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1068 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1069 | "dev": true, 1070 | "engines": { 1071 | "node": ">=0.10.0" 1072 | } 1073 | }, 1074 | "node_modules/source-map-support": { 1075 | "version": "0.5.21", 1076 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1077 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1078 | "dev": true, 1079 | "dependencies": { 1080 | "buffer-from": "^1.0.0", 1081 | "source-map": "^0.6.0" 1082 | } 1083 | }, 1084 | "node_modules/sourcemap-codec": { 1085 | "version": "1.4.8", 1086 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1087 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1088 | "dev": true 1089 | }, 1090 | "node_modules/stack-trace": { 1091 | "version": "0.0.10", 1092 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1093 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1094 | "dev": true, 1095 | "engines": { 1096 | "node": "*" 1097 | } 1098 | }, 1099 | "node_modules/streamsearch": { 1100 | "version": "1.1.0", 1101 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1102 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1103 | "dev": true, 1104 | "engines": { 1105 | "node": ">=10.0.0" 1106 | } 1107 | }, 1108 | "node_modules/to-regex-range": { 1109 | "version": "5.0.1", 1110 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1111 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1112 | "dev": true, 1113 | "dependencies": { 1114 | "is-number": "^7.0.0" 1115 | }, 1116 | "engines": { 1117 | "node": ">=8.0" 1118 | } 1119 | }, 1120 | "node_modules/undici": { 1121 | "version": "5.9.1", 1122 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.9.1.tgz", 1123 | "integrity": "sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==", 1124 | "dev": true, 1125 | "engines": { 1126 | "node": ">=12.18" 1127 | } 1128 | }, 1129 | "node_modules/urlpattern-polyfill": { 1130 | "version": "4.0.3", 1131 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1132 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1133 | "dev": true 1134 | }, 1135 | "node_modules/wrangler": { 1136 | "version": "2.0.28", 1137 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.0.28.tgz", 1138 | "integrity": "sha512-BNN+xebKF8CnZGagirNXzPbU9GU1deawiH/ASDN4h5Bi1Kgm/HpUMZpkP1LoMjtveiiS7Pa53A6x00bm2JEFlQ==", 1139 | "dev": true, 1140 | "dependencies": { 1141 | "@cloudflare/kv-asset-handler": "^0.2.0", 1142 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1143 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1144 | "blake3-wasm": "^2.1.5", 1145 | "chokidar": "^3.5.3", 1146 | "esbuild": "0.14.51", 1147 | "miniflare": "^2.7.1", 1148 | "nanoid": "^3.3.3", 1149 | "path-to-regexp": "^6.2.0", 1150 | "selfsigned": "^2.0.1", 1151 | "source-map": "^0.7.4", 1152 | "xxhash-wasm": "^1.0.1" 1153 | }, 1154 | "bin": { 1155 | "wrangler": "bin/wrangler.js", 1156 | "wrangler2": "bin/wrangler.js" 1157 | }, 1158 | "engines": { 1159 | "node": ">=16.13.0" 1160 | }, 1161 | "optionalDependencies": { 1162 | "fsevents": "~2.3.2" 1163 | } 1164 | }, 1165 | "node_modules/wrangler/node_modules/source-map": { 1166 | "version": "0.7.4", 1167 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", 1168 | "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", 1169 | "dev": true, 1170 | "engines": { 1171 | "node": ">= 8" 1172 | } 1173 | }, 1174 | "node_modules/ws": { 1175 | "version": "8.8.1", 1176 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1177 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1178 | "dev": true, 1179 | "engines": { 1180 | "node": ">=10.0.0" 1181 | }, 1182 | "peerDependencies": { 1183 | "bufferutil": "^4.0.1", 1184 | "utf-8-validate": "^5.0.2" 1185 | }, 1186 | "peerDependenciesMeta": { 1187 | "bufferutil": { 1188 | "optional": true 1189 | }, 1190 | "utf-8-validate": { 1191 | "optional": true 1192 | } 1193 | } 1194 | }, 1195 | "node_modules/xxhash-wasm": { 1196 | "version": "1.0.1", 1197 | "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.1.tgz", 1198 | "integrity": "sha512-Lc9CTvDrH2vRoiaUzz25q7lRaviMhz90pkx6YxR9EPYtF99yOJnv2cB+CQ0hp/TLoqrUsk8z/W2EN31T568Azw==", 1199 | "dev": true 1200 | }, 1201 | "node_modules/youch": { 1202 | "version": "2.2.2", 1203 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 1204 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 1205 | "dev": true, 1206 | "dependencies": { 1207 | "@types/stack-trace": "0.0.29", 1208 | "cookie": "^0.4.1", 1209 | "mustache": "^4.2.0", 1210 | "stack-trace": "0.0.10" 1211 | } 1212 | } 1213 | }, 1214 | "dependencies": { 1215 | "@cloudflare/kv-asset-handler": { 1216 | "version": "0.2.0", 1217 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", 1218 | "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", 1219 | "dev": true, 1220 | "requires": { 1221 | "mime": "^3.0.0" 1222 | } 1223 | }, 1224 | "@esbuild-plugins/node-globals-polyfill": { 1225 | "version": "0.1.1", 1226 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", 1227 | "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", 1228 | "dev": true, 1229 | "requires": {} 1230 | }, 1231 | "@esbuild-plugins/node-modules-polyfill": { 1232 | "version": "0.1.4", 1233 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", 1234 | "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", 1235 | "dev": true, 1236 | "requires": { 1237 | "escape-string-regexp": "^4.0.0", 1238 | "rollup-plugin-node-polyfills": "^0.2.1" 1239 | } 1240 | }, 1241 | "@iarna/toml": { 1242 | "version": "2.2.5", 1243 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 1244 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 1245 | "dev": true 1246 | }, 1247 | "@miniflare/cache": { 1248 | "version": "2.7.1", 1249 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.7.1.tgz", 1250 | "integrity": "sha512-QxN4yp8+cIlggbjIVP17xbSOjjJMco4coW5mXNPcTXazvqnbslwie9GDWmt4BkRvP77uwomf2CDUqEgxZC0frw==", 1251 | "dev": true, 1252 | "requires": { 1253 | "@miniflare/core": "2.7.1", 1254 | "@miniflare/shared": "2.7.1", 1255 | "http-cache-semantics": "^4.1.0", 1256 | "undici": "5.9.1" 1257 | } 1258 | }, 1259 | "@miniflare/cli-parser": { 1260 | "version": "2.7.1", 1261 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.7.1.tgz", 1262 | "integrity": "sha512-kuY6sWClFBQoc22g7P7gR3fv5dXDI8ezvPvNX6tHXPLiPxiYCoz8XTRUqG5CW12zTxrI3yPjEaTQoFlHzdnQkg==", 1263 | "dev": true, 1264 | "requires": { 1265 | "@miniflare/shared": "2.7.1", 1266 | "kleur": "^4.1.4" 1267 | } 1268 | }, 1269 | "@miniflare/core": { 1270 | "version": "2.7.1", 1271 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.7.1.tgz", 1272 | "integrity": "sha512-Pdq5+FPSg0L0/eUOKrEfGFowcmbcEXKCIJa8iYz1iA35koSytgTN+6zeuuGPGVXQbGGEPhNugWlOz4u70FJ1GA==", 1273 | "dev": true, 1274 | "requires": { 1275 | "@iarna/toml": "^2.2.5", 1276 | "@miniflare/shared": "2.7.1", 1277 | "@miniflare/watcher": "2.7.1", 1278 | "busboy": "^1.6.0", 1279 | "dotenv": "^10.0.0", 1280 | "kleur": "^4.1.4", 1281 | "set-cookie-parser": "^2.4.8", 1282 | "undici": "5.9.1", 1283 | "urlpattern-polyfill": "^4.0.3" 1284 | } 1285 | }, 1286 | "@miniflare/durable-objects": { 1287 | "version": "2.7.1", 1288 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.7.1.tgz", 1289 | "integrity": "sha512-bzTzhu9KgtBZ3itR/u/izBHBzQnxhfOt1IQcJNCM/TBwSf8wr6ztDdsTDFE0j9/oQYj4umbGynzZvYYUm/SniQ==", 1290 | "dev": true, 1291 | "requires": { 1292 | "@miniflare/core": "2.7.1", 1293 | "@miniflare/shared": "2.7.1", 1294 | "@miniflare/storage-memory": "2.7.1", 1295 | "undici": "5.9.1" 1296 | } 1297 | }, 1298 | "@miniflare/html-rewriter": { 1299 | "version": "2.7.1", 1300 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.7.1.tgz", 1301 | "integrity": "sha512-7088TlpQBXdKX1OPOL+34xKSF5IjiHyjggM7HizJG14IIw1kSiJYojqaOi5f/DxstTUJJCOIxHn3zKf6QSpukA==", 1302 | "dev": true, 1303 | "requires": { 1304 | "@miniflare/core": "2.7.1", 1305 | "@miniflare/shared": "2.7.1", 1306 | "html-rewriter-wasm": "^0.4.1", 1307 | "undici": "5.9.1" 1308 | } 1309 | }, 1310 | "@miniflare/http-server": { 1311 | "version": "2.7.1", 1312 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.7.1.tgz", 1313 | "integrity": "sha512-fcLrEVxtwMhj3qO5Wg5844s6WNTiixRjGEV/Top2TjP3CM6DtIc5l6zca4vozaTba39So627NDalLZQaCAcSBQ==", 1314 | "dev": true, 1315 | "requires": { 1316 | "@miniflare/core": "2.7.1", 1317 | "@miniflare/shared": "2.7.1", 1318 | "@miniflare/web-sockets": "2.7.1", 1319 | "kleur": "^4.1.4", 1320 | "selfsigned": "^2.0.0", 1321 | "undici": "5.9.1", 1322 | "ws": "^8.2.2", 1323 | "youch": "^2.2.2" 1324 | } 1325 | }, 1326 | "@miniflare/kv": { 1327 | "version": "2.7.1", 1328 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.7.1.tgz", 1329 | "integrity": "sha512-p3BUSgp2BK2l7GxM9wVnaXTM8/thzCzAITDbeyZLevtd8r3Vl1rE8W9Q+qrUbX454+zvHfG71O+BdtfFchgWkA==", 1330 | "dev": true, 1331 | "requires": { 1332 | "@miniflare/shared": "2.7.1" 1333 | } 1334 | }, 1335 | "@miniflare/r2": { 1336 | "version": "2.7.1", 1337 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.7.1.tgz", 1338 | "integrity": "sha512-UFqU2y4Qccto4PilHEn8JpTKi+lPZ61eV0G50Nnfnwa19yDKf0Wu6rYXecLTPetln10v6pCLvRvk4O93d99A6Q==", 1339 | "dev": true, 1340 | "requires": { 1341 | "@miniflare/shared": "2.7.1", 1342 | "undici": "5.9.1" 1343 | } 1344 | }, 1345 | "@miniflare/runner-vm": { 1346 | "version": "2.7.1", 1347 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.7.1.tgz", 1348 | "integrity": "sha512-kcntTSq38Jk81EQbEYs1wSrcziz/KO1JD1DyyDSw1C9pDSFmhusgObDW0VxaGgEVyh92No8l5CNlTjY7kjiMHw==", 1349 | "dev": true, 1350 | "requires": { 1351 | "@miniflare/shared": "2.7.1" 1352 | } 1353 | }, 1354 | "@miniflare/scheduler": { 1355 | "version": "2.7.1", 1356 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.7.1.tgz", 1357 | "integrity": "sha512-00DCtvSi0/Kamo1OLtvfG+zxAS9VqrFO8Q1Wg7yEJpJBUlnUn+oOXKT//aCpZuVBJLSf7tXxzRXJYNPpu09fwg==", 1358 | "dev": true, 1359 | "requires": { 1360 | "@miniflare/core": "2.7.1", 1361 | "@miniflare/shared": "2.7.1", 1362 | "cron-schedule": "^3.0.4" 1363 | } 1364 | }, 1365 | "@miniflare/shared": { 1366 | "version": "2.7.1", 1367 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.7.1.tgz", 1368 | "integrity": "sha512-hQsx/mt5N/zBxJ3DyAJyGMtdT07WeuU+nYiWjkIwQOkPgH/p72Xu0tdi2kO/KQogtxeT2B+eTMVXlE0JqZOyhA==", 1369 | "dev": true, 1370 | "requires": { 1371 | "kleur": "^4.1.4", 1372 | "picomatch": "^2.3.1" 1373 | } 1374 | }, 1375 | "@miniflare/sites": { 1376 | "version": "2.7.1", 1377 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.7.1.tgz", 1378 | "integrity": "sha512-b5pgVx5qifb9YejBfWjh5lnphc7wTX41CvBxssmCdQCxvQ+C5LgNelccNUvIBIMC+N5Ids+Fbd+Hx8MNGjp3iw==", 1379 | "dev": true, 1380 | "requires": { 1381 | "@miniflare/kv": "2.7.1", 1382 | "@miniflare/shared": "2.7.1", 1383 | "@miniflare/storage-file": "2.7.1" 1384 | } 1385 | }, 1386 | "@miniflare/storage-file": { 1387 | "version": "2.7.1", 1388 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.7.1.tgz", 1389 | "integrity": "sha512-6WiLGCeE1jIDJ3pp2ff1vFWCH1uf9BNWRkF3FpK7LyINzdDUlV56RtchPTBgk61oE8NYjlTqoYd4+KUvBul3/w==", 1390 | "dev": true, 1391 | "requires": { 1392 | "@miniflare/shared": "2.7.1", 1393 | "@miniflare/storage-memory": "2.7.1" 1394 | } 1395 | }, 1396 | "@miniflare/storage-memory": { 1397 | "version": "2.7.1", 1398 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.7.1.tgz", 1399 | "integrity": "sha512-/YD6PshGEQneLmPC/FO+TnhN2STXT4oTuPxVo81fZ+q/XKglTA8iULtcgmF025lZ8S871ZANfmBtUzlxZJmW8Q==", 1400 | "dev": true, 1401 | "requires": { 1402 | "@miniflare/shared": "2.7.1" 1403 | } 1404 | }, 1405 | "@miniflare/watcher": { 1406 | "version": "2.7.1", 1407 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.7.1.tgz", 1408 | "integrity": "sha512-0P0jG2IoMIQtX2JHTABY13Yq3Fs2w5gs6f/LG/X0O9pBCN3SxeQXt0bp3ELkEHjNANQWLMUs6aohb7yZ6ZTfHg==", 1409 | "dev": true, 1410 | "requires": { 1411 | "@miniflare/shared": "2.7.1" 1412 | } 1413 | }, 1414 | "@miniflare/web-sockets": { 1415 | "version": "2.7.1", 1416 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.7.1.tgz", 1417 | "integrity": "sha512-VO0BhkYDn82LTRhvK1vJA1/PA9GXMJGlkt2wYomdQFOz4Rmybau4sgVyAdKWTTYV7XexEVAVRl8BDUM97Pdxvw==", 1418 | "dev": true, 1419 | "requires": { 1420 | "@miniflare/core": "2.7.1", 1421 | "@miniflare/shared": "2.7.1", 1422 | "undici": "5.9.1", 1423 | "ws": "^8.2.2" 1424 | } 1425 | }, 1426 | "@types/stack-trace": { 1427 | "version": "0.0.29", 1428 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 1429 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 1430 | "dev": true 1431 | }, 1432 | "anymatch": { 1433 | "version": "3.1.2", 1434 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1435 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1436 | "dev": true, 1437 | "requires": { 1438 | "normalize-path": "^3.0.0", 1439 | "picomatch": "^2.0.4" 1440 | } 1441 | }, 1442 | "binary-extensions": { 1443 | "version": "2.2.0", 1444 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1445 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1446 | "dev": true 1447 | }, 1448 | "blake3-wasm": { 1449 | "version": "2.1.5", 1450 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1451 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1452 | "dev": true 1453 | }, 1454 | "braces": { 1455 | "version": "3.0.2", 1456 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1457 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1458 | "dev": true, 1459 | "requires": { 1460 | "fill-range": "^7.0.1" 1461 | } 1462 | }, 1463 | "buffer-from": { 1464 | "version": "1.1.2", 1465 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1466 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 1467 | "dev": true 1468 | }, 1469 | "busboy": { 1470 | "version": "1.6.0", 1471 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 1472 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 1473 | "dev": true, 1474 | "requires": { 1475 | "streamsearch": "^1.1.0" 1476 | } 1477 | }, 1478 | "chokidar": { 1479 | "version": "3.5.3", 1480 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1481 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1482 | "dev": true, 1483 | "requires": { 1484 | "anymatch": "~3.1.2", 1485 | "braces": "~3.0.2", 1486 | "fsevents": "~2.3.2", 1487 | "glob-parent": "~5.1.2", 1488 | "is-binary-path": "~2.1.0", 1489 | "is-glob": "~4.0.1", 1490 | "normalize-path": "~3.0.0", 1491 | "readdirp": "~3.6.0" 1492 | } 1493 | }, 1494 | "cookie": { 1495 | "version": "0.4.2", 1496 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 1497 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 1498 | "dev": true 1499 | }, 1500 | "cron-schedule": { 1501 | "version": "3.0.6", 1502 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 1503 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 1504 | "dev": true 1505 | }, 1506 | "dotenv": { 1507 | "version": "10.0.0", 1508 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 1509 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 1510 | "dev": true 1511 | }, 1512 | "esbuild": { 1513 | "version": "0.14.51", 1514 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz", 1515 | "integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==", 1516 | "dev": true, 1517 | "requires": { 1518 | "esbuild-android-64": "0.14.51", 1519 | "esbuild-android-arm64": "0.14.51", 1520 | "esbuild-darwin-64": "0.14.51", 1521 | "esbuild-darwin-arm64": "0.14.51", 1522 | "esbuild-freebsd-64": "0.14.51", 1523 | "esbuild-freebsd-arm64": "0.14.51", 1524 | "esbuild-linux-32": "0.14.51", 1525 | "esbuild-linux-64": "0.14.51", 1526 | "esbuild-linux-arm": "0.14.51", 1527 | "esbuild-linux-arm64": "0.14.51", 1528 | "esbuild-linux-mips64le": "0.14.51", 1529 | "esbuild-linux-ppc64le": "0.14.51", 1530 | "esbuild-linux-riscv64": "0.14.51", 1531 | "esbuild-linux-s390x": "0.14.51", 1532 | "esbuild-netbsd-64": "0.14.51", 1533 | "esbuild-openbsd-64": "0.14.51", 1534 | "esbuild-sunos-64": "0.14.51", 1535 | "esbuild-windows-32": "0.14.51", 1536 | "esbuild-windows-64": "0.14.51", 1537 | "esbuild-windows-arm64": "0.14.51" 1538 | } 1539 | }, 1540 | "esbuild-android-64": { 1541 | "version": "0.14.51", 1542 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz", 1543 | "integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==", 1544 | "dev": true, 1545 | "optional": true 1546 | }, 1547 | "esbuild-android-arm64": { 1548 | "version": "0.14.51", 1549 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz", 1550 | "integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==", 1551 | "dev": true, 1552 | "optional": true 1553 | }, 1554 | "esbuild-darwin-64": { 1555 | "version": "0.14.51", 1556 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz", 1557 | "integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==", 1558 | "dev": true, 1559 | "optional": true 1560 | }, 1561 | "esbuild-darwin-arm64": { 1562 | "version": "0.14.51", 1563 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz", 1564 | "integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==", 1565 | "dev": true, 1566 | "optional": true 1567 | }, 1568 | "esbuild-freebsd-64": { 1569 | "version": "0.14.51", 1570 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz", 1571 | "integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==", 1572 | "dev": true, 1573 | "optional": true 1574 | }, 1575 | "esbuild-freebsd-arm64": { 1576 | "version": "0.14.51", 1577 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz", 1578 | "integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==", 1579 | "dev": true, 1580 | "optional": true 1581 | }, 1582 | "esbuild-linux-32": { 1583 | "version": "0.14.51", 1584 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz", 1585 | "integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==", 1586 | "dev": true, 1587 | "optional": true 1588 | }, 1589 | "esbuild-linux-64": { 1590 | "version": "0.14.51", 1591 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz", 1592 | "integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==", 1593 | "dev": true, 1594 | "optional": true 1595 | }, 1596 | "esbuild-linux-arm": { 1597 | "version": "0.14.51", 1598 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz", 1599 | "integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==", 1600 | "dev": true, 1601 | "optional": true 1602 | }, 1603 | "esbuild-linux-arm64": { 1604 | "version": "0.14.51", 1605 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz", 1606 | "integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==", 1607 | "dev": true, 1608 | "optional": true 1609 | }, 1610 | "esbuild-linux-mips64le": { 1611 | "version": "0.14.51", 1612 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz", 1613 | "integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==", 1614 | "dev": true, 1615 | "optional": true 1616 | }, 1617 | "esbuild-linux-ppc64le": { 1618 | "version": "0.14.51", 1619 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz", 1620 | "integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==", 1621 | "dev": true, 1622 | "optional": true 1623 | }, 1624 | "esbuild-linux-riscv64": { 1625 | "version": "0.14.51", 1626 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz", 1627 | "integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==", 1628 | "dev": true, 1629 | "optional": true 1630 | }, 1631 | "esbuild-linux-s390x": { 1632 | "version": "0.14.51", 1633 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz", 1634 | "integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==", 1635 | "dev": true, 1636 | "optional": true 1637 | }, 1638 | "esbuild-netbsd-64": { 1639 | "version": "0.14.51", 1640 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz", 1641 | "integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==", 1642 | "dev": true, 1643 | "optional": true 1644 | }, 1645 | "esbuild-openbsd-64": { 1646 | "version": "0.14.51", 1647 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz", 1648 | "integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==", 1649 | "dev": true, 1650 | "optional": true 1651 | }, 1652 | "esbuild-sunos-64": { 1653 | "version": "0.14.51", 1654 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz", 1655 | "integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==", 1656 | "dev": true, 1657 | "optional": true 1658 | }, 1659 | "esbuild-windows-32": { 1660 | "version": "0.14.51", 1661 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz", 1662 | "integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==", 1663 | "dev": true, 1664 | "optional": true 1665 | }, 1666 | "esbuild-windows-64": { 1667 | "version": "0.14.51", 1668 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz", 1669 | "integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==", 1670 | "dev": true, 1671 | "optional": true 1672 | }, 1673 | "esbuild-windows-arm64": { 1674 | "version": "0.14.51", 1675 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz", 1676 | "integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==", 1677 | "dev": true, 1678 | "optional": true 1679 | }, 1680 | "escape-string-regexp": { 1681 | "version": "4.0.0", 1682 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1683 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1684 | "dev": true 1685 | }, 1686 | "estree-walker": { 1687 | "version": "0.6.1", 1688 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 1689 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 1690 | "dev": true 1691 | }, 1692 | "fill-range": { 1693 | "version": "7.0.1", 1694 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1695 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1696 | "dev": true, 1697 | "requires": { 1698 | "to-regex-range": "^5.0.1" 1699 | } 1700 | }, 1701 | "fsevents": { 1702 | "version": "2.3.2", 1703 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1704 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1705 | "dev": true, 1706 | "optional": true 1707 | }, 1708 | "glob-parent": { 1709 | "version": "5.1.2", 1710 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1711 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1712 | "dev": true, 1713 | "requires": { 1714 | "is-glob": "^4.0.1" 1715 | } 1716 | }, 1717 | "html-rewriter-wasm": { 1718 | "version": "0.4.1", 1719 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 1720 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 1721 | "dev": true 1722 | }, 1723 | "http-cache-semantics": { 1724 | "version": "4.1.0", 1725 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 1726 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", 1727 | "dev": true 1728 | }, 1729 | "is-binary-path": { 1730 | "version": "2.1.0", 1731 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1732 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1733 | "dev": true, 1734 | "requires": { 1735 | "binary-extensions": "^2.0.0" 1736 | } 1737 | }, 1738 | "is-extglob": { 1739 | "version": "2.1.1", 1740 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1741 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1742 | "dev": true 1743 | }, 1744 | "is-glob": { 1745 | "version": "4.0.3", 1746 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1747 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1748 | "dev": true, 1749 | "requires": { 1750 | "is-extglob": "^2.1.1" 1751 | } 1752 | }, 1753 | "is-number": { 1754 | "version": "7.0.0", 1755 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1756 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1757 | "dev": true 1758 | }, 1759 | "kleur": { 1760 | "version": "4.1.5", 1761 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1762 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1763 | "dev": true 1764 | }, 1765 | "magic-string": { 1766 | "version": "0.25.9", 1767 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", 1768 | "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", 1769 | "dev": true, 1770 | "requires": { 1771 | "sourcemap-codec": "^1.4.8" 1772 | } 1773 | }, 1774 | "mime": { 1775 | "version": "3.0.0", 1776 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1777 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1778 | "dev": true 1779 | }, 1780 | "miniflare": { 1781 | "version": "2.7.1", 1782 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.7.1.tgz", 1783 | "integrity": "sha512-O9kjSORazNCAGVkS0bRHhKGH1LcFOJZyBD0TchB02TalnQ3W21+QWO5PAXDGz/IATO8C8iXrPnN2XKDdDav2CA==", 1784 | "dev": true, 1785 | "requires": { 1786 | "@miniflare/cache": "2.7.1", 1787 | "@miniflare/cli-parser": "2.7.1", 1788 | "@miniflare/core": "2.7.1", 1789 | "@miniflare/durable-objects": "2.7.1", 1790 | "@miniflare/html-rewriter": "2.7.1", 1791 | "@miniflare/http-server": "2.7.1", 1792 | "@miniflare/kv": "2.7.1", 1793 | "@miniflare/r2": "2.7.1", 1794 | "@miniflare/runner-vm": "2.7.1", 1795 | "@miniflare/scheduler": "2.7.1", 1796 | "@miniflare/shared": "2.7.1", 1797 | "@miniflare/sites": "2.7.1", 1798 | "@miniflare/storage-file": "2.7.1", 1799 | "@miniflare/storage-memory": "2.7.1", 1800 | "@miniflare/web-sockets": "2.7.1", 1801 | "kleur": "^4.1.4", 1802 | "semiver": "^1.1.0", 1803 | "source-map-support": "^0.5.20", 1804 | "undici": "5.9.1" 1805 | } 1806 | }, 1807 | "mustache": { 1808 | "version": "4.2.0", 1809 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1810 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1811 | "dev": true 1812 | }, 1813 | "nanoid": { 1814 | "version": "3.3.4", 1815 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 1816 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 1817 | "dev": true 1818 | }, 1819 | "node-forge": { 1820 | "version": "1.3.1", 1821 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 1822 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 1823 | "dev": true 1824 | }, 1825 | "normalize-path": { 1826 | "version": "3.0.0", 1827 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1828 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1829 | "dev": true 1830 | }, 1831 | "path-to-regexp": { 1832 | "version": "6.2.1", 1833 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", 1834 | "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", 1835 | "dev": true 1836 | }, 1837 | "picomatch": { 1838 | "version": "2.3.1", 1839 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1840 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1841 | "dev": true 1842 | }, 1843 | "readdirp": { 1844 | "version": "3.6.0", 1845 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1846 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1847 | "dev": true, 1848 | "requires": { 1849 | "picomatch": "^2.2.1" 1850 | } 1851 | }, 1852 | "rollup-plugin-inject": { 1853 | "version": "3.0.2", 1854 | "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", 1855 | "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", 1856 | "dev": true, 1857 | "requires": { 1858 | "estree-walker": "^0.6.1", 1859 | "magic-string": "^0.25.3", 1860 | "rollup-pluginutils": "^2.8.1" 1861 | } 1862 | }, 1863 | "rollup-plugin-node-polyfills": { 1864 | "version": "0.2.1", 1865 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", 1866 | "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", 1867 | "dev": true, 1868 | "requires": { 1869 | "rollup-plugin-inject": "^3.0.0" 1870 | } 1871 | }, 1872 | "rollup-pluginutils": { 1873 | "version": "2.8.2", 1874 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 1875 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 1876 | "dev": true, 1877 | "requires": { 1878 | "estree-walker": "^0.6.1" 1879 | } 1880 | }, 1881 | "selfsigned": { 1882 | "version": "2.0.1", 1883 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", 1884 | "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", 1885 | "dev": true, 1886 | "requires": { 1887 | "node-forge": "^1" 1888 | } 1889 | }, 1890 | "semiver": { 1891 | "version": "1.1.0", 1892 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1893 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1894 | "dev": true 1895 | }, 1896 | "set-cookie-parser": { 1897 | "version": "2.5.1", 1898 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 1899 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", 1900 | "dev": true 1901 | }, 1902 | "source-map": { 1903 | "version": "0.6.1", 1904 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1905 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1906 | "dev": true 1907 | }, 1908 | "source-map-support": { 1909 | "version": "0.5.21", 1910 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1911 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1912 | "dev": true, 1913 | "requires": { 1914 | "buffer-from": "^1.0.0", 1915 | "source-map": "^0.6.0" 1916 | } 1917 | }, 1918 | "sourcemap-codec": { 1919 | "version": "1.4.8", 1920 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1921 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1922 | "dev": true 1923 | }, 1924 | "stack-trace": { 1925 | "version": "0.0.10", 1926 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1927 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1928 | "dev": true 1929 | }, 1930 | "streamsearch": { 1931 | "version": "1.1.0", 1932 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1933 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1934 | "dev": true 1935 | }, 1936 | "to-regex-range": { 1937 | "version": "5.0.1", 1938 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1939 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1940 | "dev": true, 1941 | "requires": { 1942 | "is-number": "^7.0.0" 1943 | } 1944 | }, 1945 | "undici": { 1946 | "version": "5.9.1", 1947 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.9.1.tgz", 1948 | "integrity": "sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==", 1949 | "dev": true 1950 | }, 1951 | "urlpattern-polyfill": { 1952 | "version": "4.0.3", 1953 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1954 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1955 | "dev": true 1956 | }, 1957 | "wrangler": { 1958 | "version": "2.0.28", 1959 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.0.28.tgz", 1960 | "integrity": "sha512-BNN+xebKF8CnZGagirNXzPbU9GU1deawiH/ASDN4h5Bi1Kgm/HpUMZpkP1LoMjtveiiS7Pa53A6x00bm2JEFlQ==", 1961 | "dev": true, 1962 | "requires": { 1963 | "@cloudflare/kv-asset-handler": "^0.2.0", 1964 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1965 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1966 | "blake3-wasm": "^2.1.5", 1967 | "chokidar": "^3.5.3", 1968 | "esbuild": "0.14.51", 1969 | "fsevents": "~2.3.2", 1970 | "miniflare": "^2.7.1", 1971 | "nanoid": "^3.3.3", 1972 | "path-to-regexp": "^6.2.0", 1973 | "selfsigned": "^2.0.1", 1974 | "source-map": "^0.7.4", 1975 | "xxhash-wasm": "^1.0.1" 1976 | }, 1977 | "dependencies": { 1978 | "source-map": { 1979 | "version": "0.7.4", 1980 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", 1981 | "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", 1982 | "dev": true 1983 | } 1984 | } 1985 | }, 1986 | "ws": { 1987 | "version": "8.8.1", 1988 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1989 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1990 | "dev": true, 1991 | "requires": {} 1992 | }, 1993 | "xxhash-wasm": { 1994 | "version": "1.0.1", 1995 | "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.1.tgz", 1996 | "integrity": "sha512-Lc9CTvDrH2vRoiaUzz25q7lRaviMhz90pkx6YxR9EPYtF99yOJnv2cB+CQ0hp/TLoqrUsk8z/W2EN31T568Azw==", 1997 | "dev": true 1998 | }, 1999 | "youch": { 2000 | "version": "2.2.2", 2001 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 2002 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 2003 | "dev": true, 2004 | "requires": { 2005 | "@types/stack-trace": "0.0.29", 2006 | "cookie": "^0.4.1", 2007 | "mustache": "^4.2.0", 2008 | "stack-trace": "0.0.10" 2009 | } 2010 | } 2011 | } 2012 | } 2013 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "durable-stream", 3 | "version": "1.0.0-beta", 4 | "module": "./src/main.mjs", 5 | "devDependencies": { 6 | "miniflare": "^2.7.1", 7 | "wrangler": "^2.0.28" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "deploy": "wrangler publish src/main.mjs", 12 | "dev": "miniflare -w", 13 | "logs": "wrangler tail" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/voxoco/cf-test.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/voxoco/cf-test/issues" 23 | }, 24 | "homepage": "https://github.com/voxoco/cf-test#readme" 25 | } 26 | -------------------------------------------------------------------------------- /src/handleErrors.mjs: -------------------------------------------------------------------------------- 1 | export default async (req, func) => { 2 | try { 3 | return await func(); 4 | } catch (err) { 5 | 6 | if (req.headers.get("Upgrade") !== "websocket") return new Response(err.stack, {status: 500}); 7 | 8 | // Annoyingly, if we return an HTTP error in response to a WebSocket request, Chrome devtools 9 | // won't show us the response body! So... let's send a WebSocket response with an error 10 | // frame instead. 11 | let pair = new WebSocketPair(); 12 | pair[1].accept(); 13 | pair[1].send(JSON.stringify({error: err.stack})); 14 | pair[1].close(1011, "Uncaught exception during session setup"); 15 | return new Response(null, { status: 101, webSocket: pair[0] }); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main.mjs: -------------------------------------------------------------------------------- 1 | import handleErrors from "./handleErrors.mjs"; 2 | import router from "./router/index.mjs"; 3 | 4 | // Worker 5 | export default { 6 | async fetch(req, env) { 7 | return await handleErrors(req, async () => { 8 | // We have received an HTTP request. Parse the URL and route the request 9 | return await router(req, env); 10 | }) 11 | } 12 | } 13 | 14 | // Durable Object 15 | export class DurableStream { 16 | constructor(controller, env) { 17 | // `controller.storage` provides access to our durable storage, kv, etc... 18 | this.storage = controller.storage; 19 | 20 | // Initialize the sequence number from storage 21 | this.sequence = -1; 22 | 23 | // `env` is our environment bindings 24 | this.env = env; 25 | 26 | // We will put the websocket objects for each client, along with metadata, into `sessions` 27 | this.sessions = {}; 28 | 29 | // For promised messages 30 | this.waitingOperations = {}; 31 | 32 | // For message ids 33 | const rand = Math.random(); 34 | this.sMsgId = rand; 35 | 36 | // Object to hold arbitrary data (like some state) 37 | this.state = {}; 38 | } 39 | 40 | // Initialize the sequence number from storage 41 | async initSequence() { 42 | const lastKey = await this.storage.list({limit: 1, reverse: true, prefix: '1'}); 43 | this.sequence = lastKey.keys().next().value ? parseInt(lastKey.keys().next().value) : 100000000000; 44 | } 45 | 46 | // Initialize the state object from storage 47 | async initState() { 48 | const state = await this.storage.get('state'); 49 | this.state = state ? JSON.parse(state) : {lock: false, sequence: 100000000000}; 50 | await this.storage.put('state', JSON.stringify(this.state)); 51 | } 52 | 53 | // The system will call fetch() whenever an HTTP request is sent to this object 54 | async fetch(req) { 55 | return await handleErrors(req, async () => { 56 | const url = new URL(req.url); 57 | 58 | // Could be the first time we are running, so initialize the sequence number 59 | if (this.sequence === -1) await this.initSequence(); 60 | 61 | if (!Object.keys(this.state).length) await this.initState(); 62 | 63 | switch (url.pathname) { 64 | case "/websocket": 65 | // This request is to `/stream//websocket`. We will upgrade it to a websocket 66 | if (req.headers.get("Upgrade") !== "websocket") return new Response("Expected websocket", {status: 400}); 67 | 68 | // Get the ip for fun 69 | const ip = req.headers.get('CF-Connecting-IP'); 70 | 71 | // Create the websocket pair 72 | const pair = new WebSocketPair(); 73 | 74 | // We're going to take pair[1] as our end, and return pair[0] to the client. 75 | await this.handleSession(pair[1], ip); 76 | 77 | // Now we return the other end of the pair to the client 78 | return new Response(null, { status: 101, webSocket: pair[0] }); 79 | 80 | default: 81 | return new Response("Not found", { status: 404 }); 82 | } 83 | }) 84 | } 85 | 86 | // Handle promise sessions 87 | async handleSession(ws, ip) { 88 | ws.accept(); 89 | 90 | // Add the websocket to our sessions list 91 | const sid = Math.random().toString(16).slice(2); 92 | this.sessions[sid] = {ws, ip, hasListener: false, sid}; 93 | console.log(`New session: ${sid}`); 94 | 95 | // On "close", remove the WebSocket from the sessions list and broadcast 96 | const closeHandler = async () => { 97 | console.log(`Session closed: ${sid}`); 98 | delete this.sessions[sid]; 99 | } 100 | 101 | ws.addEventListener("close", closeHandler); 102 | ws.addEventListener("error", closeHandler); 103 | 104 | // Listen for messages 105 | ws.addEventListener('message', async (msg) => { 106 | await this.handleMessage(msg, sid); 107 | }) 108 | } 109 | 110 | async handleMessage(msg, sid) { 111 | let json = JSON.parse(msg.data); 112 | 113 | if (!json.data) return; 114 | 115 | // This must be a message from us to the client that is waiting for a response 116 | if (json.sMsgId) { 117 | this.waitingOperations[json.sMsgId].resolveMe(json); 118 | delete this.waitingOperations[json.sMsgId]; 119 | return; 120 | } 121 | 122 | if (!json.cMsgId) return; 123 | 124 | // This is a message from a client that needs a response 125 | json.ip = this.sessions[sid].ip; 126 | json.sequence = this.sequence; 127 | 128 | // First check for any commands 129 | if (json.data?.cmd) { 130 | console.log(`Received command: ${json.data.cmd} from ${sid}`); 131 | 132 | switch(json.data.cmd) { 133 | case 'getStreamInfo': 134 | this.sessions[sid].ws.send(JSON.stringify(json)); 135 | return; 136 | case 'subscribe': 137 | this.sessions[sid].ws.send(JSON.stringify(json)); 138 | await this.subscribe(json.data.startSequence, sid); 139 | this.sessions[sid].hasListener = true; 140 | return; 141 | case 'unsubscribe': 142 | this.sessions[sid].hasListener = false; 143 | this.sessions[sid].ws.send(JSON.stringify(json)); 144 | return; 145 | case 'deleteMessages': 146 | await this.delete(json.data.sequence); 147 | this.sessions[sid].ws.send(JSON.stringify(json)); 148 | return; 149 | case 'getState': 150 | json.state = this.state; 151 | this.sessions[sid].ws.send(JSON.stringify(json)); 152 | return; 153 | case 'putState': 154 | this.state = json.data.state; 155 | await this.storage.put('state', JSON.stringify(this.state)); 156 | this.sessions[sid].ws.send(JSON.stringify(json)); 157 | return; 158 | default: 159 | console.log('Unknown command', json.data.cmd); 160 | } 161 | } 162 | 163 | // Message from a client that needs to be broadcast 164 | const newSequence = this.sequence + 1; 165 | json.sequence = newSequence; 166 | await this.storage.put(`${newSequence}`, JSON.stringify(json)); 167 | 168 | // Now increment the sequence number 169 | this.sequence = newSequence; 170 | 171 | // Now that the message is stored, reply to the client 172 | this.sessions[sid].ws.send(JSON.stringify(json)); 173 | 174 | // Broadcast the message to all clients 175 | await this.broadcast(json); 176 | } 177 | 178 | async publish(msg, sid) { 179 | return new Promise((resolve) => { 180 | this.sMsgId++; 181 | msg.pub = 1; 182 | this.waitingOperations[this.sMsgId] = { resolveMe: resolve, ...msg, sMsgId: this.sMsgId }; 183 | this.sessions[sid].ws.send(JSON.stringify({...msg, sMsgId: this.sMsgId})); 184 | }) 185 | } 186 | 187 | // Broadcast a message to all clients. 188 | async broadcast(msg) { 189 | 190 | // Get a list of session id's to that are listening 191 | const sessions = Object.keys(this.sessions).filter((sid) => this.sessions[sid].hasListener); 192 | 193 | // Early return if there are no sessions with a listener 194 | if (!sessions.length) return; 195 | 196 | console.log(`Broadcasting to ${sessions.length} sessions`); 197 | 198 | // Send the message to each session 199 | let promises = []; 200 | for (const sid of sessions) promises.push(this.publish(msg, sid)); 201 | 202 | // Wait for all the messages to be sent 203 | await Promise.all(promises); 204 | } 205 | 206 | // Handle subscriptions 207 | async subscribe(start, sid) { 208 | if (start <= 0) start = 0; 209 | 210 | console.log(`Subscribing ${sid} to messages from sequence ${start}`); 211 | 212 | // If the startSequence is greater than or equal to this.sequence then there are no messages to send 213 | if (start >= this.sequence) { 214 | console.log(`No backlog of messages to send to ${sid}`); 215 | return; 216 | } 217 | 218 | // Take the start position and retrieve all messages after it 219 | const end = `${this.sequence + 1}`; 220 | // Since storage api returns the map in lexicographic order, we need all the messages... 221 | let msgs = await this.storage.list({start, end, prefix: '1'}); 222 | 223 | if (![...msgs.keys()].length) return; 224 | 225 | // We have messages to send, so send them 226 | console.log(`Sending ${[...msgs.keys()].length} messages to consumer`); 227 | for (const [key, value] of msgs) if (value !== '') await this.publish(JSON.parse(value), sid); 228 | 229 | // Now that the consumer is caught up to `end` run this again to send any messages that were missed 230 | if (parseInt(end) >= this.sequence) return; 231 | 232 | console.log(`Consumer is still behind by ${this.sequence - parseInt(end)} messages`); 233 | start = parseInt(end); 234 | await this.subscribe(start, sid); 235 | } 236 | 237 | // Handle delete from storage 238 | async delete(sequence) { 239 | if (sequence <= 0) return; 240 | 241 | // Delete the messages from storage up to `sequence` 242 | let msgs = await this.storage.list({start: 0, end: sequence + 1, prefix: '1'}); 243 | 244 | if (![...msgs.keys()].length) return; 245 | 246 | // Delete the messages 247 | console.log(`Deleting ${[...msgs.keys()].length} messages from storage`); 248 | for (const [key, value] of msgs) { 249 | if (parseInt(key) >= this.sequence) { 250 | console.log(`Attempt to delete messages > or = current sequence`); 251 | await this.storage.put(key, ''); 252 | continue; 253 | } 254 | await this.storage.delete(key); 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /src/router/index.mjs: -------------------------------------------------------------------------------- 1 | import stream from './stream.mjs'; 2 | import r2 from './r2.mjs'; 3 | 4 | export default async (req, env) => { 5 | 6 | const url = new URL(req.url); 7 | const path = url.pathname.slice(1).split('/'); 8 | 9 | // Make sure we have an api key in the url 10 | if (!url.searchParams.has('apiKey')) return new Response('Unauthorized', {status: 401}); 11 | 12 | // Make sure the api key is valid 13 | if (url.searchParams.get('apiKey') !== env.API_KEY) return new Response('Unauthorized', {status: 401}); 14 | 15 | if (!path[0]) return new Response('Error', {status: 500}); 16 | 17 | // Handle requests to `stream` endpoint 18 | if (path[0] === 'stream') return await stream(req, env); 19 | 20 | // Handle requests to `r2` endpoint 21 | if (path[0] === 'r2') return await r2(req, env); 22 | 23 | // Handle any other requests 24 | return new Response('Not Found', {status: 404}); 25 | } -------------------------------------------------------------------------------- /src/router/r2.mjs: -------------------------------------------------------------------------------- 1 | export default async (req, env) => { 2 | const url = new URL(req.url); 3 | // Remove the first part of the path `/r2/` 4 | const key = url.pathname.split('/').slice(2).join('/'); 5 | 6 | console.log(`${req.method} object '${key}' ${req.url}`); 7 | 8 | // Handle HEAD requests 9 | if (req.method === 'HEAD') { 10 | if (key === '') return new Response(undefined, {status: 400}); 11 | 12 | const object = await env.bucket.head(key); 13 | if (!object) return await objectNotFound(key); 14 | 15 | const headers = new Headers(); 16 | object.writeHttpMetadata(headers); 17 | headers.set('etag', object.httpEtag); 18 | 19 | return new Response(null, {headers}); 20 | } 21 | 22 | // Handle GET requests 23 | if (req.method === 'GET') { 24 | if (key === '') { 25 | const options = { 26 | prefix: url.searchParams.get('prefix') ?? undefined, 27 | delimiter: url.searchParams.get('delimiter') ?? undefined, 28 | cursor: url.searchParams.get('cursor') ?? undefined, 29 | include: ['customMetadata', 'httpMetadata'], 30 | } 31 | console.log(JSON.stringify(options)); 32 | 33 | const listing = await env.bucket.list(options); 34 | return new Response(JSON.stringify(listing), {headers: {'content-type': 'application/json; charset=UTF-8'}}); 35 | } 36 | 37 | const range = await parseRange(req.headers.get('range')); 38 | const object = await env.bucket.get(key, {range, onlyIf: req.headers}); 39 | 40 | if (!object) return await objectNotFound(key); 41 | 42 | const headers = new Headers(); 43 | object.writeHttpMetadata(headers); 44 | headers.set('etag', object.httpEtag); 45 | if (range) { headers.set("content-range", `bytes ${range.offset}-${range.end}/${object.size}`) } 46 | const status = object.body ? (range ? 206 : 200) : 304; 47 | 48 | return new Response(object.body, {headers, status}); 49 | } 50 | 51 | // Handle POST requests 52 | if (req.method === 'POST') { 53 | const object = await env.bucket.put(key, req.body, {httpMetadata: req.headers}) 54 | 55 | return new Response(`Put ${key} successfully!`, { headers: { 'etag': object.httpEtag } }); 56 | } 57 | 58 | // Handle DELETE requests 59 | if (req.method === 'DELETE') { 60 | await env.bucket.delete(key); 61 | return new Response(); 62 | } 63 | 64 | return new Response('Unsupported method', {status: 400}); 65 | } 66 | 67 | const parseRange = async (encoded) => { 68 | if (encoded === null) return; 69 | 70 | const parts = encoded.split("bytes=")[1]?.split("-") ?? [] 71 | if (parts.length !== 2) throw new Error('Not supported to skip specifying the beginning/ending byte at this time'); 72 | 73 | return { offset: Number(parts[0]), end: Number(parts[1]), length: Number(parts[1]) + 1 - Number(parts[0]) } 74 | } 75 | 76 | const objectNotFound = async (key) => { 77 | return new Response(`R2 object "${key}" not found`, { 78 | status: 404, 79 | headers: {'content-type': 'text/html; charset=UTF-8'} 80 | }) 81 | } -------------------------------------------------------------------------------- /src/router/stream.mjs: -------------------------------------------------------------------------------- 1 | export default async (req, env) => { 2 | 3 | const url = new URL(req.url); 4 | const path = url.pathname.slice(1).split('/'); 5 | 6 | // Make sure this is a GET request 7 | if (req.method !== 'GET') return new Response('Not Found', {status: 404}); 8 | 9 | // Request should be `/stream/` 10 | if (!path[1]) return new Response('Error', {status: 500}); 11 | 12 | // Get the stream object id from the path 13 | const id = env.durableStream.idFromName(path[1]); 14 | // Get the durable object stub for this stream subject 15 | const obj = env.durableStream.get(id); 16 | 17 | // Add `/websocket` to the path to get the websocket endpoint 18 | const wsUrl = new URL(req.url); 19 | wsUrl.pathname = '/websocket'; 20 | 21 | console.log(`New connection for stream ${path[1]}`); 22 | 23 | // Send the request to the object 24 | return obj.fetch(wsUrl, req); 25 | } -------------------------------------------------------------------------------- /wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "durable_stream" 2 | compatibility_date = "2022-08-15" 3 | main = "src/main" 4 | 5 | [build.upload] 6 | format = "modules" 7 | 8 | [durable_objects] 9 | bindings = [{name = "durableStream", class_name = "DurableStream"}] 10 | 11 | [[migrations]] 12 | tag = "v1" # Should be unique for each entry 13 | new_classes = ["DurableStream"] 14 | 15 | [[r2_buckets]] 16 | binding = 'bucket' 17 | bucket_name = 'durable-stream-bucket' 18 | 19 | [miniflare] 20 | durable_objects_persist = true 21 | r2_persist = true --------------------------------------------------------------------------------