├── 02-create-a-supabase-project ├── wrangler.toml ├── src │ └── index.js ├── package.json ├── README.md ├── .gitignore └── package-lock.json ├── 03-query-supabase-from-cloudflare-worker ├── wrangler.toml ├── package.json ├── src │ └── index.js ├── README.md └── .gitignore ├── 01-create-cloudflare-worker-with-wrangler-cli ├── wrangler.toml ├── src │ └── index.js ├── package.json ├── README.md └── .gitignore ├── 04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router ├── wrangler.toml ├── package.json ├── src │ └── index.js ├── README.md └── .gitignore ├── 09-revalidate-stale-data-by-id ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── README.md └── .gitignore ├── 08-cache-busting-with-kv-stores-and-supabase ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── README.md └── .gitignore ├── 05-bind-kv-store-to-cloudflare-worker ├── wrangler.toml ├── package.json ├── src │ └── index.js ├── README.md └── .gitignore ├── 06-read-and-write-to-kv-cache-from-cloudflare-worker ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── README.md └── .gitignore ├── 07-cache-supabase-response-at-the-edge-with-kv-storage ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── .gitignore └── README.md ├── 11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── README.md └── .gitignore ├── 10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase ├── src │ ├── utils │ │ └── cache.js │ └── index.js ├── wrangler.toml ├── package.json ├── README.md └── .gitignore ├── .gitignore └── README.md /02-create-a-supabase-project/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | -------------------------------------------------------------------------------- /03-query-supabase-from-cloudflare-worker/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | -------------------------------------------------------------------------------- /01-create-cloudflare-worker-with-wrangler-cli/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | -------------------------------------------------------------------------------- /02-create-a-supabase-project/src/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | return new Response("Hello eggies 🥚!"); 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /01-create-cloudflare-worker-with-wrangler-cli/src/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | async fetch(request) { 3 | return new Response("Hello eggies 🥚!"); 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /02-create-a-supabase-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /05-bind-kv-store-to-cloudflare-worker/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /01-create-cloudflare-worker-with-wrangler-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-07" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/src/utils/cache.js: -------------------------------------------------------------------------------- 1 | export const readFrom = async (cache, path) => { 2 | const data = await cache.get(path); 3 | return JSON.parse(data); 4 | }; 5 | 6 | export const writeTo = async (cache, path, data) => { 7 | await cache.put(path, JSON.stringify(data)); 8 | }; 9 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "supabase-at-the-edge" 2 | main = "src/index.js" 3 | compatibility_date = "2022-07-26" 4 | 5 | kv_namespaces = [ 6 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 7 | ] 8 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /05-bind-kv-store-to-cloudflare-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /03-query-supabase-from-cloudflare-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.23" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "wrangler": "2.0.16" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "start": "wrangler dev", 10 | "deploy": "wrangler publish" 11 | }, 12 | "dependencies": { 13 | "@supabase/supabase-js": "^1.35.4", 14 | "itty-router": "^2.6.1", 15 | "itty-router-extras": "^0.4.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /03-query-supabase-from-cloudflare-worker/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export default { 4 | async fetch(request, { SUPABASE_URL, SUPABASE_ANON_KEY }) { 5 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 6 | 7 | const { data } = await supabase.from("articles").select("*"); 8 | return new Response(JSON.stringify(data), { 9 | headers: { 10 | "Content-Type": "application/json", 11 | }, 12 | }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /05-bind-kv-store-to-cloudflare-worker/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | 5 | const router = new Router(); 6 | 7 | router.get( 8 | "/articles", 9 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 10 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 11 | 12 | const { data } = await supabase.from("articles").select("*"); 13 | return json(data); 14 | } 15 | ); 16 | 17 | router.get( 18 | "/articles/:id", 19 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 20 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 21 | const { id } = request.params; 22 | 23 | const { data } = await supabase 24 | .from("articles") 25 | .select("*") 26 | .match({ id }) 27 | .single(); 28 | 29 | if (!data) { 30 | return status(404, "Not found"); 31 | } 32 | 33 | return json(data); 34 | } 35 | ); 36 | 37 | router.all("*", () => status(404, "Not found")); 38 | 39 | export default { 40 | fetch: router.handle, 41 | }; 42 | -------------------------------------------------------------------------------- /04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | 5 | const router = new Router(); 6 | 7 | router.get( 8 | "/articles", 9 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 10 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 11 | 12 | const { data } = await supabase.from("articles").select("*"); 13 | return json(data); 14 | } 15 | ); 16 | 17 | router.get( 18 | "/articles/:id", 19 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 20 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 21 | const { id } = request.params; 22 | 23 | const { data } = await supabase 24 | .from("articles") 25 | .select("*") 26 | .match({ id }) 27 | .single(); 28 | 29 | if (!data) { 30 | return status(404, "Not found"); 31 | } 32 | 33 | return json(data); 34 | } 35 | ); 36 | 37 | router.all("*", () => status(404, "Not found")); 38 | 39 | export default { 40 | fetch: router.handle, 41 | }; 42 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 23 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 24 | 25 | const { data } = await supabase.from("articles").select("*"); 26 | return json(data); 27 | } 28 | ); 29 | 30 | router.get( 31 | "/articles/:id", 32 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 33 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 34 | const { id } = request.params; 35 | 36 | const { data } = await supabase 37 | .from("articles") 38 | .select("*") 39 | .match({ id }) 40 | .single(); 41 | 42 | if (!data) { 43 | return status(404, "Not found"); 44 | } 45 | 46 | return json(data); 47 | } 48 | ); 49 | 50 | router.all("*", () => status(404, "Not found")); 51 | 52 | export default { 53 | fetch: router.handle, 54 | }; 55 | -------------------------------------------------------------------------------- /01-create-cloudflare-worker-with-wrangler-cli/README.md: -------------------------------------------------------------------------------- 1 | # Create a Cloudflare Worker with the Wrangler CLI 2 | 3 | **[📹 Video](https://egghead.io/lessons/cloudflare-create-a-cloudflare-worker-with-the-wrangler-cli?af=9qsk0a)** 4 | 5 | Cloudflare Workers are serverless functions that run at the Edge! 🔪 6 | 7 | The Wrangler CLI is a tool for interfacing with your Cloudflare account, and creating or publishing your Workers. 8 | 9 | In this video, we use the Wrangler CLI to log in to our Cloudflare account and authorize the `wrangler` command to make changes on our behalf. 10 | 11 | Additionally, we create a worker and host it locally with the `npx wrangler dev` command. We then demonstrate making a GET request to our Cloudflare Worker from the browser, and confirm it is sending the correct response. 12 | 13 | By modifying the response locally, we can see that changes are reflected any time we refresh the browser. 14 | 15 | ## Code Snippets 16 | 17 | **Login to Cloudflare with Wrangler CLI** 18 | 19 | ```bash 20 | npx wrangler login 21 | ``` 22 | 23 | **Create Cloudflare Worker** 24 | 25 | ```bash 26 | npx wrangler init supabase-at-the-edge 27 | ``` 28 | 29 | **Run wrangler development server** 30 | 31 | ```bash 32 | npx wrangler dev 33 | ``` 34 | 35 | ## Resources 36 | 37 | - [Cloudflare Wrangler docs](https://developers.cloudflare.com/workers/wrangler/get-started/) 38 | 39 | --- 40 | 41 | [👉 Next lesson](/02-create-a-supabase-project) 42 | 43 | --- 44 | 45 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 46 | -------------------------------------------------------------------------------- /02-create-a-supabase-project/README.md: -------------------------------------------------------------------------------- 1 | # Create a Supabase project 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-create-a-supabase-project-2fd6bc77?af=9qsk0a)** 4 | 5 | [Supabase](https://app.supabase.com/) is a suite of open-source tools wrapping a PostgreSQL database. It provides the building blocks of an app - database hosting, auth, file storage, realtime and edge functions - so we can focus on the thing that makes our app unique. 6 | 7 | In this video, we sign up for a free Supabase account, create a basic schema for a blog, and populate it with test data. 8 | 9 | ## Code Snippets 10 | 11 | **Create the table** 12 | 13 | ```sql 14 | create table if not exists articles ( 15 | id uuid default uuid_generate_v4() primary key, 16 | created_at timestamp with time zone default timezone('utc'::text, now()) not null, 17 | title text not null, 18 | content text not null, 19 | is_published bool default false not null 20 | ); 21 | ``` 22 | 23 | **Insert test data** 24 | 25 | ```sql 26 | insert into articles(title, content) 27 | values 28 | ('First blog', 'This is my very first blog'), 29 | ('Second blog', 'I am really enjoying writing blogs'), 30 | ('Third blog', 'Okay, this is getting hard to maintain'); 31 | ``` 32 | 33 | ## Resources 34 | 35 | - [Create a Supabase project](https://app.supabase.com/) 36 | - [Supabase docs](https://supabase.com/docs) 37 | 38 | --- 39 | 40 | [👉 Next lesson](/03-query-supabase-from-cloudflare-worker) 41 | 42 | --- 43 | 44 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 45 | -------------------------------------------------------------------------------- /05-bind-kv-store-to-cloudflare-worker/README.md: -------------------------------------------------------------------------------- 1 | # Bind a KV store to Cloudflare Worker 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-bind-a-kv-store-to-cloudflare-worker?af=9qsk0a)** 4 | 5 | KV Storage is a cache that Cloudflare makes available within our Workers. It replicates across multiple CDN nodes, making it a super performant way to cache data. 6 | 7 | In this lesson, we create a new KV store using the Wrangler CLI, and bind it to our Cloudflare worker using the `wrangler.toml` configuration file. 8 | 9 | Additionally, we create a `preview` store to use in development mode. 10 | 11 | ## Code Snippets 12 | 13 | **Create a KV Store** 14 | 15 | ```bash 16 | npx wrangler kv:namespace create "ARTICLES" 17 | ``` 18 | 19 | **Create a preview KV Store** 20 | 21 | ```bash 22 | npx wrangler kv:namespace create "ARTICLES" --preview 23 | ``` 24 | 25 | **Bind KV to Cloudflare Worker** 26 | 27 | ```toml 28 | kv_namespaces = [ 29 | { binding = "replace-with-your-kv-name", id = "replace-with-your-kv-production-id", preview_id = "replace-with-your-kv-preview-id" } 30 | ] 31 | ``` 32 | 33 | **Run wrangler development server** 34 | 35 | ```bash 36 | npx wrangler dev 37 | ``` 38 | 39 | ## Resources 40 | 41 | - [How KV works](https://developers.cloudflare.com/workers/learning/how-kv-works/) 42 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 43 | 44 | --- 45 | 46 | [👉 Next lesson](/06-read-and-write-to-kv-cache-from-cloudflare-worker) 47 | 48 | --- 49 | 50 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 51 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Cache-busting with KV stores and Supabase 2 | 3 | **[📹 Video](https://egghead.io/lessons/cloudflare-cache-busting-with-cloudflare-kv-stores-and-supabase?af=9qsk0a)** 4 | 5 | Cache Busting or Invalidation, is the process of clearing stale data from a cache. After the first request, the response data from Supabase is cached in our KV store, and never fetched again. This means if data in the database changes, our KV store will never be updated. 6 | 7 | In this lesson, we create a `revalidate` route, which fetches fresh data from Supabase and updates the cache. 8 | 9 | ## Code Snippets 10 | 11 | **Revalidate cache for articles route** 12 | 13 | ```javascript 14 | router.get( 15 | "/revalidate", 16 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 17 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 18 | const { data: articles } = await supabase.from("articles").select("*"); 19 | await writeTo(ARTICLES, "/articles", articles); 20 | return json({ received: true }); 21 | } 22 | ); 23 | ``` 24 | 25 | **Run wrangler development server** 26 | 27 | ```bash 28 | npx wrangler dev 29 | ``` 30 | 31 | ## Resources 32 | 33 | - [Supabase.js docs](https://github.com/supabase/supabase-js) 34 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 35 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 36 | 37 | --- 38 | 39 | [👉 Next lesson](/09-revalidate-stale-data-by-id) 40 | 41 | --- 42 | 43 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 44 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 23 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 24 | 25 | if (cachedArticles) { 26 | console.log("sending the cache"); 27 | return json(cachedArticles); 28 | } 29 | 30 | console.log("fetching fresh articles"); 31 | 32 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 33 | 34 | const { data } = await supabase.from("articles").select("*"); 35 | await writeTo(ARTICLES, "/articles", data); 36 | return json(data); 37 | } 38 | ); 39 | 40 | router.get( 41 | "/articles/:id", 42 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 43 | const { id } = request.params; 44 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 45 | 46 | if (cachedArticle) { 47 | console.log("sending the cache"); 48 | return json(cachedArticle); 49 | } 50 | 51 | console.log("fetching fresh article"); 52 | 53 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 54 | 55 | const { data } = await supabase 56 | .from("articles") 57 | .select("*") 58 | .match({ id }) 59 | .single(); 60 | 61 | await writeTo(ARTICLES, `/articles/${id}`, data); 62 | 63 | return json(data); 64 | } 65 | ); 66 | 67 | router.all("*", () => status(404, "Not found")); 68 | 69 | export default { 70 | fetch: router.handle, 71 | }; 72 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/README.md: -------------------------------------------------------------------------------- 1 | # Revalidate stale data by ID 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-revalidate-stale-data-by-id?af=9qsk0a)** 4 | 5 | Handling revalidation of a particular article requires us to pass through an ID as part of the request's body. Therefore, we need to change this from a GET request to a POST request and use the `withContent` middleware to parse the request's body into a JSON object. 6 | 7 | Additionally, we fetch data for this specific article from Supabase, using its ID, and update the cache for that path. 8 | 9 | We can no longer test this using a browser, as it can only send GET requests. Therefore, we use a VS Code extension called Thunder Client to make the POST request and pass along the article's ID in the request's body. 10 | 11 | ## Code Snippets 12 | 13 | **Update Revalidate route for single article** 14 | 15 | ```javascript 16 | router.post( 17 | "/revalidate", 18 | withContent, 19 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 20 | const { id } = request.content; 21 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 22 | 23 | const { data: article } = await supabase 24 | .from("articles") 25 | .select("*") 26 | .match({ id }) 27 | .single(); 28 | await writeTo(ARTICLES, `/articles/${id}`, article); 29 | 30 | const { data: articles } = await supabase.from("articles").select("*"); 31 | await writeTo(ARTICLES, "/articles", articles); 32 | 33 | return json({ received: true }); 34 | } 35 | ); 36 | ``` 37 | 38 | **Run wrangler development server** 39 | 40 | ```bash 41 | npx wrangler dev 42 | ``` 43 | 44 | ## Resources 45 | 46 | - [Supabase.js docs](https://github.com/supabase/supabase-js) 47 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 48 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 49 | - [Thunder Client VS Code extension](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client) 50 | 51 | --- 52 | 53 | [👉 Next lesson](/10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase) 54 | 55 | --- 56 | 57 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 58 | -------------------------------------------------------------------------------- /04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router/README.md: -------------------------------------------------------------------------------- 1 | # Proxy Supabase requests with Cloudflare Workers and Itty Router 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-proxy-supabase-requests-with-cloudflare-workers-and-itty-router?af=9qsk0a)** 4 | 5 | Itty Router is a tiny router package that has a similar API to Express. In this video, we install Itty Router and the Extras packages. 6 | 7 | Additionally, we refactor our existing worker to use the router, and declare a dynamic route to fetch a single article. 8 | 9 | To handle paths and articles that don't exist, we add a catch all route that returns a 404 status. 10 | 11 | Lastly, we use the `json` helper to automatically stringify JSON objects and set the `Content-Type` header for `application/json`. 12 | 13 | ## Code Snippets 14 | 15 | **Install Itty Router and extras** 16 | 17 | ```bash 18 | npm i itty-router itty-router-extras 19 | ``` 20 | 21 | **Run wrangler development server** 22 | 23 | ```bash 24 | npx wrangler dev 25 | ``` 26 | 27 | **GET route for all articles** 28 | 29 | ```javascript 30 | router.get( 31 | "/articles", 32 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 33 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 34 | 35 | const { data } = await supabase.from("articles").select("*"); 36 | return json(data); 37 | } 38 | ); 39 | ``` 40 | 41 | **GET route for one article** 42 | 43 | ```javascript 44 | router.get( 45 | "/articles/:id", 46 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY }) => { 47 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 48 | const { id } = request.params; 49 | 50 | const { data } = await supabase 51 | .from("articles") 52 | .select("*") 53 | .match({ id }) 54 | .single(); 55 | 56 | if (!data) { 57 | return status(404, "Not found"); 58 | } 59 | 60 | return json(data); 61 | } 62 | ); 63 | ``` 64 | 65 | **Catch all route for 404** 66 | 67 | ```javascript 68 | router.all("*", () => status(404, "Not found")); 69 | ``` 70 | 71 | ## Resources 72 | 73 | - [Itty Router repo](https://github.com/kwhitley/itty-router) 74 | - [Itty Router Extras repo](https://github.com/kwhitley/itty-router-extras) 75 | 76 | --- 77 | 78 | [👉 Next lesson](/05-bind-kv-store-to-cloudflare-worker) 79 | 80 | --- 81 | 82 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 83 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 23 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 24 | 25 | if (cachedArticles) { 26 | console.log("sending the cache"); 27 | return json(cachedArticles); 28 | } 29 | 30 | console.log("fetching fresh articles"); 31 | 32 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 33 | 34 | const { data } = await supabase.from("articles").select("*"); 35 | await writeTo(ARTICLES, "/articles", data); 36 | return json(data); 37 | } 38 | ); 39 | 40 | router.get( 41 | "/articles/:id", 42 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 43 | const { id } = request.params; 44 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 45 | 46 | if (cachedArticle) { 47 | console.log("sending the cache"); 48 | return json(cachedArticle); 49 | } 50 | 51 | console.log("fetching fresh article"); 52 | 53 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 54 | 55 | const { data } = await supabase 56 | .from("articles") 57 | .select("*") 58 | .match({ id }) 59 | .single(); 60 | 61 | await writeTo(ARTICLES, `/articles/${id}`, data); 62 | 63 | return json(data); 64 | } 65 | ); 66 | 67 | router.get( 68 | "/revalidate", 69 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 70 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 71 | const { data } = await supabase.from("articles").select("*"); 72 | await writeTo(ARTICLES, "/articles", data); 73 | return json({ received: true }); 74 | } 75 | ); 76 | 77 | router.all("*", () => status(404, "Not found")); 78 | 79 | export default { 80 | fetch: router.handle, 81 | }; 82 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/README.md: -------------------------------------------------------------------------------- 1 | # Use waitUntil to perform work after Cloudflare Worker returns response 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-use-waituntil-to-perform-work-after-cloudflare-worker-returns-response?af=9qsk0a)** 4 | 5 | The `waitUntil` function allows us to continue performing work in our Cloudflare Worker, after a response has been sent back to the client. 6 | 7 | In this lesson, we modify the `revalidate` route to send a response immediately, and then continue on to fetch new data and refresh our KV store. 8 | 9 | Finally, we use the Thunder Client extension to simulate a POST request to the `revalidate` route, and confirm that this receives a response before the cache is updated. 10 | 11 | ## Code Snippets 12 | 13 | **Update Revalidate route to respond immediately** 14 | 15 | ```javascript 16 | router.post( 17 | "/revalidate", 18 | withContent, 19 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }, context) => { 20 | const updateCache = async () => { 21 | const { type, record, old_record } = request.content; 22 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 23 | 24 | if (type === "INSERT" || type === "UPDATE") { 25 | await writeTo(ARTICLES, `/articles/${record.id}`, record); 26 | } 27 | 28 | if (type === "DELETE") { 29 | await ARTICLES.delete(`/articles/${old_record.id}`); 30 | } 31 | 32 | const { data: articles } = await supabase.from("articles").select("*"); 33 | await writeTo(ARTICLES, "/articles", articles); 34 | console.log("updated cache"); 35 | }; 36 | 37 | context.waitUntil(updateCache()); 38 | 39 | console.log("sending response"); 40 | 41 | return json({ received: true }); 42 | } 43 | ); 44 | ``` 45 | 46 | **Run wrangler development server** 47 | 48 | ```bash 49 | npx wrangler dev 50 | ``` 51 | 52 | ## Resources 53 | 54 | - [Cloudflare waitUntil docs](https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/) 55 | - [Supabase.js docs](https://github.com/supabase/supabase-js) 56 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 57 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 58 | - [Thunder Client VS Code extension](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client) 59 | 60 | --- 61 | 62 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 63 | -------------------------------------------------------------------------------- /03-query-supabase-from-cloudflare-worker/README.md: -------------------------------------------------------------------------------- 1 | # Query Supabase from Cloudflare Worker 2 | 3 | **[📹 Video](https://egghead.io/lessons/cloudflare-query-supabase-from-cloudflare-worker?af=9qsk0a)** 4 | 5 | Supabase JS is an NPM package which provides a simple interface from JavaScript to our Supabase project. It allows us to query and mutate data using its Object Relational Mapping (ORM) syntax, and subscribe to realtime events. 6 | 7 | In this video, we install the Supabase JS package and create a new client using our project's URL and Anon Key. These can be found in the Supabase dashboard for our project, under `Settings > API`. 8 | 9 | We store these values as secrets in our Cloudflare account, and use them to instantiate a new Supabase client. 10 | 11 | Additionally, we write a query to select all of our articles from our Supabase instance, and send them back as the response from our Cloudflare Worker. 12 | 13 | In order to send a JSON response, we first stringify the object we get back from Supabase, and then set a `Content-Type` header to notify the browser that this will be a type of `application/json`. 14 | 15 | ## Code Snippets 16 | 17 | **Install Supabase JS** 18 | 19 | ```bash 20 | npm i @supabase-supabase-js 21 | ``` 22 | 23 | **Create a Cloudflare secret** 24 | 25 | ```bash 26 | npx wrangler secret put NAME 27 | ``` 28 | 29 | **Add a secret for SUPABASE_URL** 30 | 31 | ```bash 32 | npx wrangler secret put SUPABASE_URL 33 | ``` 34 | 35 | **Run wrangler development server** 36 | 37 | ```bash 38 | npx wrangler dev 39 | ``` 40 | 41 | **Add a secret for SUPABASE_ANON_KEY** 42 | 43 | ```bash 44 | npx wrangler secret put SUPABASE_ANON_KEY 45 | ``` 46 | 47 | **Query data from Supabase** 48 | 49 | ```javascript 50 | const { data } = await supabase.from("articles").select("*"); 51 | ``` 52 | 53 | **Send JSON response** 54 | 55 | ```javascript 56 | return new Response(JSON.stringify(data), { 57 | headers: { 58 | "Content-Type": "application/json", 59 | }, 60 | }); 61 | ``` 62 | 63 | ## Resources 64 | 65 | - [Selecting data with Supabase JS](https://supabase.com/docs/reference/javascript/select) 66 | - [Introducing Secrets and Environment Variables to Cloudflare Workers](https://blog.cloudflare.com/workers-secrets-environment/) 67 | - [Cloudflare docs for sending JSON responses](https://developers.cloudflare.com/workers/examples/return-json/) 68 | 69 | --- 70 | 71 | [👉 Next lesson](/04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router) 72 | 73 | --- 74 | 75 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 76 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/README.md: -------------------------------------------------------------------------------- 1 | # Read and write to KV cache from Cloudflare Worker 2 | 3 | **[📹 Video](https://egghead.io/lessons/cloudflare-read-and-write-kv-storage-cache-from-cloudflare-worker?af=9qsk0a)** 4 | 5 | KV Storage is a read-optimized, eventually consistent cache that can be accessed from Cloudflare Workers. The interface for reading and writing is similar to `localStorage` in the browser. 6 | 7 | In this lesson, we create a `read-kv` route for reading from the KV store. This automatically gets passed any KV stores that have been bound to the Cloudflare Worker. Initially, the store is empty, so returns the value `null`. 8 | 9 | Additionally, we create a `write-kv` route to populate the store with test data. 10 | 11 | To reduce the overhead of stringifying and parsing JSON objects, we create two helper functions to easily read and write values to any KV store. 12 | 13 | Lastly, we publish a production version of our Cloudflare Worker, to confirm that writing these value locally - to our preview store - does not affect our production store. 14 | 15 | ## Code Snippets 16 | 17 | **readFrom helper function** 18 | 19 | ```javascript 20 | export const readFrom = async (cache, path) => { 21 | const data = await cache.get(path); 22 | return JSON.parse(data); 23 | }; 24 | ``` 25 | 26 | **writeTo helper function** 27 | 28 | ```javascript 29 | export const writeTo = async (cache, path, data) => { 30 | await cache.put(path, JSON.stringify(data)); 31 | }; 32 | ``` 33 | 34 | **Read KV route** 35 | 36 | ```javascript 37 | router.get("/read-kv", async (request, { ARTICLES }) => { 38 | const articles = await readFrom(ARTICLES, "/articles"); 39 | return json(articles); 40 | }); 41 | ``` 42 | 43 | **Write KV route** 44 | 45 | ```javascript 46 | router.get("/write-kv", async (request, { ARTICLES }) => { 47 | const articles = [{ title: "test3" }, { title: "test4" }]; 48 | await writeTo(ARTICLES, "/articles", articles); 49 | 50 | return json(articles); 51 | }); 52 | ``` 53 | 54 | **Publish Cloudflare Worker** 55 | 56 | ```bash 57 | npx wrangler publish 58 | ``` 59 | 60 | **Run wrangler development server** 61 | 62 | ```bash 63 | npx wrangler dev 64 | ``` 65 | 66 | ## Resources 67 | 68 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 69 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 70 | 71 | --- 72 | 73 | [👉 Next lesson](/07-cache-supabase-response-at-the-edge-with-kv-storage) 74 | 75 | --- 76 | 77 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 78 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 23 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 24 | 25 | if (cachedArticles) { 26 | console.log("sending the cache"); 27 | return json(cachedArticles); 28 | } 29 | 30 | console.log("fetching fresh articles"); 31 | 32 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 33 | 34 | const { data } = await supabase.from("articles").select("*"); 35 | await writeTo(ARTICLES, "/articles", data); 36 | return json(data); 37 | } 38 | ); 39 | 40 | router.get( 41 | "/articles/:id", 42 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 43 | const { id } = request.params; 44 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 45 | 46 | if (cachedArticle) { 47 | console.log("sending the cache"); 48 | return json(cachedArticle); 49 | } 50 | 51 | console.log("fetching fresh article"); 52 | 53 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 54 | 55 | const { data } = await supabase 56 | .from("articles") 57 | .select("*") 58 | .match({ id }) 59 | .single(); 60 | 61 | await writeTo(ARTICLES, `/articles/${id}`, data); 62 | 63 | return json(data); 64 | } 65 | ); 66 | 67 | router.post( 68 | "/revalidate", 69 | withContent, 70 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 71 | const { id } = request.content; 72 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 73 | 74 | const { data: article } = await supabase 75 | .from("articles") 76 | .select("*") 77 | .match({ id }) 78 | .single(); 79 | await writeTo(ARTICLES, `/articles/${id}`, article); 80 | 81 | const { data: articles } = await supabase.from("articles").select("*"); 82 | await writeTo(ARTICLES, "/articles", articles); 83 | 84 | return json({ received: true }); 85 | } 86 | ); 87 | 88 | router.all("*", () => status(404, "Not found")); 89 | 90 | export default { 91 | fetch: router.handle, 92 | }; 93 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Automatically revalidate KV Store cache on change with Database Webhooks in Supabase 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase-5c330bb2?af=9qsk0a)** 4 | 5 | Supabase Function Hooks allow us to subscribe to change events in the database - such as `insert`, `update` and `delete` - and make a HTTP request with the changed data. 6 | 7 | In this lesson, we refactor our `revalidate` route to handle inserts, updates and deletes. 8 | 9 | Additionally, we create a Function Hook to subscribe to all change events on the `articles` table, and automatically send a POST request to the `revalidate` route of our Cloudflare Worker. 10 | 11 | This automatically refreshes the KV store anytime a value is changed in the database, and decouples the revalidation of our cache, from our user requesting data. This means, theoretically, users should never have to wait for a request to the Supabase origin server, as the cache is automatically populated, updated and cleared to remain in sync with the database. 12 | 13 | ## Code Snippets 14 | 15 | **Update Revalidate route to handle insert, update and delete** 16 | 17 | ```javascript 18 | router.post( 19 | "/revalidate", 20 | withContent, 21 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 22 | const { type, record, old_record } = request.content; 23 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 24 | 25 | if (type === "INSERT" || type === "UPDATE") { 26 | await writeTo(ARTICLES, `/articles/${record.id}`, record); 27 | } 28 | 29 | if (type === "DELETE") { 30 | await ARTICLES.delete(`/articles/${old_record.id}`); 31 | } 32 | 33 | const { data: articles } = await supabase.from("articles").select("*"); 34 | await writeTo(ARTICLES, "/articles", articles); 35 | 36 | return json({ received: true }); 37 | } 38 | ); 39 | ``` 40 | 41 | **Run wrangler development server** 42 | 43 | ```bash 44 | npx wrangler dev 45 | ``` 46 | 47 | **Publish Cloudflare Worker with Wrangler CLI** 48 | 49 | ```bash 50 | npx wrangler publish 51 | ``` 52 | 53 | ## Resources 54 | 55 | - [Supabase Functions blog](https://supabase.com/blog/2021/07/30/supabase-functions-updates#function-hooks-alpha) 56 | - [Supabase.js docs](https://github.com/supabase/supabase-js) 57 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 58 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 59 | 60 | --- 61 | 62 | [👉 Next lesson](/11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response) 63 | 64 | --- 65 | 66 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 67 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 23 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 24 | 25 | if (cachedArticles) { 26 | console.log("sending the cache"); 27 | return json(cachedArticles); 28 | } 29 | 30 | console.log("fetching fresh articles"); 31 | 32 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 33 | 34 | const { data } = await supabase.from("articles").select("*"); 35 | await writeTo(ARTICLES, "/articles", data); 36 | return json(data); 37 | } 38 | ); 39 | 40 | router.get( 41 | "/articles/:id", 42 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 43 | const { id } = request.params; 44 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 45 | 46 | if (cachedArticle) { 47 | console.log("sending the cache"); 48 | return json(cachedArticle); 49 | } 50 | 51 | console.log("fetching fresh article"); 52 | 53 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 54 | 55 | const { data } = await supabase 56 | .from("articles") 57 | .select("*") 58 | .match({ id }) 59 | .single(); 60 | 61 | await writeTo(ARTICLES, `/articles/${id}`, data); 62 | 63 | return json(data); 64 | } 65 | ); 66 | 67 | router.post( 68 | "/revalidate", 69 | withContent, 70 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 71 | const { type, record, old_record } = request.content; 72 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 73 | 74 | if (type === "INSERT" || type === "UPDATE") { 75 | await writeTo(ARTICLES, `/articles/${record.id}`, record); 76 | } 77 | 78 | if (type === "DELETE") { 79 | await ARTICLES.delete(`/articles/${old_record.id}`); 80 | } 81 | 82 | const { data: articles } = await supabase.from("articles").select("*"); 83 | await writeTo(ARTICLES, "/articles", articles); 84 | 85 | return json({ received: true }); 86 | } 87 | ); 88 | 89 | router.all("*", () => status(404, "Not found")); 90 | 91 | export default { 92 | fetch: router.handle, 93 | }; 94 | -------------------------------------------------------------------------------- /02-create-a-supabase-project/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /09-revalidate-stale-data-by-id/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /05-bind-kv-store-to-cloudflare-worker/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /03-query-supabase-from-cloudflare-worker/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /08-cache-busting-with-kv-stores-and-supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /01-create-cloudflare-worker-with-wrangler-cli/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /06-read-and-write-to-kv-cache-from-cloudflare-worker/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response/src/index.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import { Router } from "itty-router"; 3 | import { json, status, withContent } from "itty-router-extras"; 4 | import { readFrom, writeTo } from "./utils/cache"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/read-kv", async (request, { ARTICLES }) => { 9 | const articles = await readFrom(ARTICLES, "/articles"); 10 | return json(articles); 11 | }); 12 | 13 | router.get("/write-kv", async (request, { ARTICLES }) => { 14 | const articles = [{ title: "test3" }, { title: "test4" }]; 15 | await writeTo(ARTICLES, "/articles", articles); 16 | 17 | return json(articles); 18 | }); 19 | 20 | router.get( 21 | "/articles", 22 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 23 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 24 | 25 | if (cachedArticles) { 26 | console.log("sending the cache"); 27 | return json(cachedArticles); 28 | } 29 | 30 | console.log("fetching fresh articles"); 31 | 32 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 33 | 34 | const { data } = await supabase.from("articles").select("*"); 35 | await writeTo(ARTICLES, "/articles", data); 36 | return json(data); 37 | } 38 | ); 39 | 40 | router.get( 41 | "/articles/:id", 42 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 43 | const { id } = request.params; 44 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 45 | 46 | if (cachedArticle) { 47 | console.log("sending the cache"); 48 | return json(cachedArticle); 49 | } 50 | 51 | console.log("fetching fresh article"); 52 | 53 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 54 | 55 | const { data } = await supabase 56 | .from("articles") 57 | .select("*") 58 | .match({ id }) 59 | .single(); 60 | 61 | await writeTo(ARTICLES, `/articles/${id}`, data); 62 | 63 | return json(data); 64 | } 65 | ); 66 | 67 | router.post( 68 | "/revalidate", 69 | withContent, 70 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }, context) => { 71 | const updateCache = async () => { 72 | const { type, record, old_record } = request.content; 73 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 74 | 75 | if (type === "INSERT" || type === "UPDATE") { 76 | await writeTo(ARTICLES, `/articles/${record.id}`, record); 77 | } 78 | 79 | if (type === "DELETE") { 80 | await ARTICLES.delete(`/articles/${old_record.id}`); 81 | } 82 | 83 | const { data: articles } = await supabase.from("articles").select("*"); 84 | await writeTo(ARTICLES, "/articles", articles); 85 | console.log("updated cache"); 86 | }; 87 | 88 | context.waitUntil(updateCache()); 89 | 90 | console.log("sending response"); 91 | 92 | return json({ received: true }); 93 | } 94 | ); 95 | 96 | router.all("*", () => status(404, "Not found")); 97 | 98 | export default { 99 | fetch: router.handle, 100 | }; 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | */**/node_modules 5 | */**/.pnp 6 | */**.pnp.js 7 | 8 | # testing 9 | */**/coverage 10 | 11 | # next.js 12 | */**/.next/ 13 | */**/out/ 14 | 15 | # production 16 | */**/build 17 | 18 | # misc 19 | .DS_Store 20 | */**/.DS_Store 21 | */**/*.pem 22 | 23 | # debug 24 | */**/npm-debug.log* 25 | */**/yarn-debug.log* 26 | */**/yarn-error.log* 27 | 28 | # local env files 29 | */**/.env.local 30 | */**/.env.development.local 31 | */**/.env.test.local 32 | */**/.env.production.local 33 | 34 | # vercel 35 | */**/.vercel 36 | 37 | # temp 38 | /example 39 | 40 | # Logs 41 | 42 | logs 43 | _.log 44 | npm-debug.log_ 45 | yarn-debug.log* 46 | yarn-error.log* 47 | lerna-debug.log* 48 | .pnpm-debug.log* 49 | 50 | # Diagnostic reports (https://nodejs.org/api/report.html) 51 | 52 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 53 | 54 | # Runtime data 55 | 56 | pids 57 | _.pid 58 | _.seed 59 | \*.pid.lock 60 | 61 | # Directory for instrumented libs generated by jscoverage/JSCover 62 | 63 | lib-cov 64 | 65 | # Coverage directory used by tools like istanbul 66 | 67 | coverage 68 | \*.lcov 69 | 70 | # nyc test coverage 71 | 72 | .nyc_output 73 | 74 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 75 | 76 | .grunt 77 | 78 | # Bower dependency directory (https://bower.io/) 79 | 80 | bower_components 81 | 82 | # node-waf configuration 83 | 84 | .lock-wscript 85 | 86 | # Compiled binary addons (https://nodejs.org/api/addons.html) 87 | 88 | build/Release 89 | 90 | # Dependency directories 91 | 92 | node_modules/ 93 | jspm_packages/ 94 | 95 | # Snowpack dependency directory (https://snowpack.dev/) 96 | 97 | web_modules/ 98 | 99 | # TypeScript cache 100 | 101 | \*.tsbuildinfo 102 | 103 | # Optional npm cache directory 104 | 105 | .npm 106 | 107 | # Optional eslint cache 108 | 109 | .eslintcache 110 | 111 | # Optional stylelint cache 112 | 113 | .stylelintcache 114 | 115 | # Microbundle cache 116 | 117 | .rpt2_cache/ 118 | .rts2_cache_cjs/ 119 | .rts2_cache_es/ 120 | .rts2_cache_umd/ 121 | 122 | # Optional REPL history 123 | 124 | .node_repl_history 125 | 126 | # Output of 'npm pack' 127 | 128 | \*.tgz 129 | 130 | # Yarn Integrity file 131 | 132 | .yarn-integrity 133 | 134 | # dotenv environment variable files 135 | 136 | .env 137 | .env.development.local 138 | .env.test.local 139 | .env.production.local 140 | .env.local 141 | 142 | # parcel-bundler cache (https://parceljs.org/) 143 | 144 | .cache 145 | .parcel-cache 146 | 147 | # Next.js build output 148 | 149 | .next 150 | out 151 | 152 | # Nuxt.js build / generate output 153 | 154 | .nuxt 155 | dist 156 | 157 | # Gatsby files 158 | 159 | .cache/ 160 | 161 | # Comment in the public line in if your project uses Gatsby and not Next.js 162 | 163 | # https://nextjs.org/blog/next-9-1#public-directory-support 164 | 165 | # public 166 | 167 | # vuepress build output 168 | 169 | .vuepress/dist 170 | 171 | # vuepress v2.x temp and cache directory 172 | 173 | .temp 174 | .cache 175 | 176 | # Docusaurus cache and generated files 177 | 178 | .docusaurus 179 | 180 | # Serverless directories 181 | 182 | .serverless/ 183 | 184 | # FuseBox cache 185 | 186 | .fusebox/ 187 | 188 | # DynamoDB Local files 189 | 190 | .dynamodb/ 191 | 192 | # TernJS port file 193 | 194 | .tern-port 195 | 196 | # Stores VSCode versions used for testing VSCode extensions 197 | 198 | .vscode-test 199 | 200 | # yarn v2 201 | 202 | .yarn/cache 203 | .yarn/unplugged 204 | .yarn/build-state.yml 205 | .yarn/install-state.gz 206 | .pnp.\* 207 | 208 | # wrangler project 209 | 210 | .dev.vars 211 | -------------------------------------------------------------------------------- /07-cache-supabase-response-at-the-edge-with-kv-storage/README.md: -------------------------------------------------------------------------------- 1 | # Cache Supabase response at the Edge with KV storage 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-cache-supabase-response-at-the-edge-with-kv-storage?af=9qsk0a)** 4 | 5 | KV Storage allows us to cache a value as close as possible to our Cloudflare Worker. The first time a user navigates to a route, we don't have the data in the cache, so we need to make a request to the Supabase origin server. 6 | 7 | This server could be on the other side of the world and take a long time to respond. Therefore, if we cache the response we get back for a particular route, there is no need to make an additional request to the origin. 8 | 9 | In this lesson, we implement cache-aware routes for fetching many articles or a single article. This first checks whether we have the cached value, and sends it back immediately if we do. If not, it will make a request for fresh data from Supabase, and cache the response for the next visitor. 10 | 11 | Additionally, we simulate what a slow request might be like, as we might not experience this with super fast computers geographically close to our hosting servers. 12 | 13 | ## Code Snippets 14 | 15 | **Cache-aware many articles route** 16 | 17 | ```javascript 18 | router.get( 19 | "/articles", 20 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 21 | const cachedArticles = await readFrom(ARTICLES, "/articles"); 22 | 23 | if (cachedArticles) { 24 | console.log("sending the cache"); 25 | return json(cachedArticles); 26 | } 27 | 28 | console.log("fetching fresh articles"); 29 | 30 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 31 | 32 | const { data } = await supabase.from("articles").select("*"); 33 | await writeTo(ARTICLES, "/articles", data); 34 | return json(data); 35 | } 36 | ); 37 | ``` 38 | 39 | **Cache-aware one article route** 40 | 41 | ```javascript 42 | router.get( 43 | "/articles/:id", 44 | async (request, { SUPABASE_URL, SUPABASE_ANON_KEY, ARTICLES }) => { 45 | const { id } = request.params; 46 | const cachedArticle = await readFrom(ARTICLES, `/articles/${id}`); 47 | 48 | if (cachedArticle) { 49 | console.log("sending the cache"); 50 | return json(cachedArticle); 51 | } 52 | 53 | console.log("fetching fresh article"); 54 | 55 | const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 56 | 57 | const { data } = await supabase 58 | .from("articles") 59 | .select("*") 60 | .match({ id }) 61 | .single(); 62 | 63 | await writeTo(ARTICLES, `/articles/${id}`, data); 64 | 65 | return json(data); 66 | } 67 | ); 68 | ``` 69 | 70 | **Simulate 3 second delay** 71 | 72 | ```javascript 73 | const data = await new Promise((resolve) => { 74 | setTimeout(async () => { 75 | const { data } = await supabase.from("articles").select("*"); 76 | resolve(data); 77 | }, 3000); 78 | }); 79 | ``` 80 | 81 | **Delete key from KV Store** 82 | 83 | ```bash 84 | npx wrangler kv:key delete --binding=ARTICLES '/articles' --preview 85 | ``` 86 | 87 | **Run wrangler development server** 88 | 89 | ```bash 90 | npx wrangler dev 91 | ``` 92 | 93 | ## Resources 94 | 95 | - [Supabase.js docs](https://github.com/supabase/supabase-js) 96 | - [Wrangler CLI docs](https://developers.cloudflare.com/workers/wrangler/commands/) 97 | - [KV Storage docs](https://developers.cloudflare.com/workers/runtime-apis/kv/) 98 | 99 | --- 100 | 101 | [👉 Next lesson](/08-cache-busting-with-kv-stores-and-supabase) 102 | 103 | --- 104 | 105 | Enjoying the course? Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to the [YouTube channel](https://www.youtube.com/c/jonmeyers). 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Supabase data at the Edge with Cloudflare Workers and KV Storage](https://egghead.io/courses/cache-supabase-data-at-the-edge-with-cloudflare-workers-and-kv-storage-883c7959?af=9qsk0a) 2 | 3 | 4 | Cache Supabase data at the Edge with Cloudflare Workers and KV Storage 5 | 6 | 7 | > This repo accompanies this [free egghead course](https://egghead.io/courses/cache-supabase-data-at-the-edge-with-cloudflare-workers-and-kv-storage-883c7959?af=9qsk0a). 8 | 9 | ## 🔍 About 10 | 11 | Cloudflare Workers are serverless functions that run at the Edge! 🔪 This means they are running as geographically close as possible to the users of our application. This is super performant if you can perform all of the work at the edge, but what if you need to query a database? 🤔 12 | 13 | Databases store the most important asset of our apps - their data - so need to remain reliable and consistent. Therefore, they are usually hosted in a single origin location. Unfortunately, this means as soon as our Cloudflare Worker needs to hit the database, we lose some of the performance benefit of running at the edge - especially if the user's location is on the other side of the world to our origin database. 14 | 15 | So how can we reduce the number of trips to the origin server? 💡 16 | 17 | Caching! 🧠 18 | 19 | KV Storage is a special cache available to Cloudflare Workers, that allows us to "remember" some data at the edge. It is an eventually consistent cache - meaning it propagates changes across Cloudflare's CDN network, but data might be out of sync in different regions for a short period of time. 20 | 21 | The special thing about pairing this with Supabase is we can subscribe to changes in the database - such as `insert`, `update` or `delete` and, not just bust the KV Storage cache, but automatically refresh the data without the user at the other end of the request, waiting for fresh data from the origin server! 🎉 22 | 23 | ## 🎓 Instructor 24 | 25 | [Jon Meyers](https://jonmeyers.io) is a Software Engineer, Educator and Hip Hop Producer from Melbourne, Australia. He's passionate about web development and enabling others to build amazing things! He is currently working as a Developer Advocate at Supabase, showing just how awesome (and not that scary 👻) databases can be! 26 | 27 | [Jon's courses at egghead.](https://egghead.io/q/resources-by-jon-meyers) 28 | 29 | Follow Jon Meyers on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to his [YouTube channel](https://www.youtube.com/c/jonmeyers). 30 | 31 | ## 🗺 Table of Contents 32 | 33 | 1. [Create a Cloudflare Worker with the Wrangler CLI](/01-create-cloudflare-worker-with-wrangler-cli) 34 | 2. [Create a Supabase project](/02-create-a-supabase-project) 35 | 3. [Query Supabase from Cloudflare Worker](/03-query-supabase-from-cloudflare-worker) 36 | 4. [Proxy Supabase requests with Cloudflare Workers and Itty Router](/04-proxy-supabase-requests-with-cloudflare-workers-and-itty-router) 37 | 5. [Bind a KV store to Cloudflare Worker](/05-bind-kv-store-to-cloudflare-worker) 38 | 6. [Read and write to KV cache from Cloudflare Worker](/06-read-and-write-to-kv-cache-from-cloudflare-worker) 39 | 7. [Cache Supabase response at the Edge with KV storage](/07-cache-supabase-response-at-the-edge-with-kv-storage) 40 | 8. [Cache-busting with KV stores and Supabase](/08-cache-busting-with-kv-stores-and-supabase) 41 | 9. [Revalidate stale data by ID](/09-revalidate-stale-data-by-id) 42 | 10. [Automatically revalidate KV Store cache on change with Database Webhooks in Supabase](/10-automatically-revalidate-kv-store-cache-on-change-with-database-webhooks-in-supabase) 43 | 11. [Use waitUntil to perform work after Cloudflare Worker returns response](/11-use-waitUntil-to-perform-work-after-cloudflare-worker-returns-response) 44 | -------------------------------------------------------------------------------- /02-create-a-supabase-project/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-at-the-edge", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "supabase-at-the-edge", 9 | "version": "0.0.0", 10 | "devDependencies": { 11 | "wrangler": "2.0.23" 12 | } 13 | }, 14 | "node_modules/@cloudflare/kv-asset-handler": { 15 | "version": "0.2.0", 16 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", 17 | "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", 18 | "dev": true, 19 | "dependencies": { 20 | "mime": "^3.0.0" 21 | } 22 | }, 23 | "node_modules/@esbuild-plugins/node-globals-polyfill": { 24 | "version": "0.1.1", 25 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", 26 | "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", 27 | "dev": true, 28 | "peerDependencies": { 29 | "esbuild": "*" 30 | } 31 | }, 32 | "node_modules/@esbuild-plugins/node-modules-polyfill": { 33 | "version": "0.1.4", 34 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", 35 | "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", 36 | "dev": true, 37 | "dependencies": { 38 | "escape-string-regexp": "^4.0.0", 39 | "rollup-plugin-node-polyfills": "^0.2.1" 40 | }, 41 | "peerDependencies": { 42 | "esbuild": "*" 43 | } 44 | }, 45 | "node_modules/@iarna/toml": { 46 | "version": "2.2.5", 47 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 48 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 49 | "dev": true 50 | }, 51 | "node_modules/@miniflare/cache": { 52 | "version": "2.6.0", 53 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.6.0.tgz", 54 | "integrity": "sha512-4oh8MgpquoxaslI7Z8sMzmEZR0Dc+L3aEh69o9d8ZCs4nUdOENnfKlY50O5nEnL7nhhyAljkMBaXD2wAH2DLeQ==", 55 | "dev": true, 56 | "dependencies": { 57 | "@miniflare/core": "2.6.0", 58 | "@miniflare/shared": "2.6.0", 59 | "http-cache-semantics": "^4.1.0", 60 | "undici": "5.5.1" 61 | }, 62 | "engines": { 63 | "node": ">=16.7" 64 | } 65 | }, 66 | "node_modules/@miniflare/cli-parser": { 67 | "version": "2.6.0", 68 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.6.0.tgz", 69 | "integrity": "sha512-dJDoIPAUqWhzvBHHyqyhobdzDedBYRWZ4yItBi9m4MTU/EneLJ5jryB340SwUnmtBMZxUh/LWdAuUEkKpdVNyA==", 70 | "dev": true, 71 | "dependencies": { 72 | "@miniflare/shared": "2.6.0", 73 | "kleur": "^4.1.4" 74 | }, 75 | "engines": { 76 | "node": ">=16.7" 77 | } 78 | }, 79 | "node_modules/@miniflare/core": { 80 | "version": "2.6.0", 81 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.6.0.tgz", 82 | "integrity": "sha512-CmofhIRot++GI7NHPMwzNb65+0hWLN186L91BrH/doPVHnT/itmEfzYQpL9bFLD0c/i14dfv+IUNetDdGEBIrw==", 83 | "dev": true, 84 | "dependencies": { 85 | "@iarna/toml": "^2.2.5", 86 | "@miniflare/shared": "2.6.0", 87 | "@miniflare/watcher": "2.6.0", 88 | "busboy": "^1.6.0", 89 | "dotenv": "^10.0.0", 90 | "kleur": "^4.1.4", 91 | "set-cookie-parser": "^2.4.8", 92 | "undici": "5.5.1", 93 | "urlpattern-polyfill": "^4.0.3" 94 | }, 95 | "engines": { 96 | "node": ">=16.7" 97 | } 98 | }, 99 | "node_modules/@miniflare/durable-objects": { 100 | "version": "2.6.0", 101 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.6.0.tgz", 102 | "integrity": "sha512-uzWoGFtkIIh3m3HAzqd5f86nOSC0xFli6dq2q7ilE3UjgouOcLqObxJyE/IzvSwsj4DUWFv6//YDfHihK2fGAA==", 103 | "dev": true, 104 | "dependencies": { 105 | "@miniflare/core": "2.6.0", 106 | "@miniflare/shared": "2.6.0", 107 | "@miniflare/storage-memory": "2.6.0", 108 | "undici": "5.5.1" 109 | }, 110 | "engines": { 111 | "node": ">=16.7" 112 | } 113 | }, 114 | "node_modules/@miniflare/html-rewriter": { 115 | "version": "2.6.0", 116 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.6.0.tgz", 117 | "integrity": "sha512-+JqFlIDLzstb/Spj+j/kI6uHzolrqjsMks3Tf24Q4YFo9YYdZguqUFcDz2yr79ZTP/SKXaZH+AYqosnJps4dHQ==", 118 | "dev": true, 119 | "dependencies": { 120 | "@miniflare/core": "2.6.0", 121 | "@miniflare/shared": "2.6.0", 122 | "html-rewriter-wasm": "^0.4.1", 123 | "undici": "5.5.1" 124 | }, 125 | "engines": { 126 | "node": ">=16.7" 127 | } 128 | }, 129 | "node_modules/@miniflare/http-server": { 130 | "version": "2.6.0", 131 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.6.0.tgz", 132 | "integrity": "sha512-FhcAVIpipMEzMCsJBc/b0JhNEJ66GPX60vA2NcqjGKHYbwoPCPlwCFQq2giPzW/R95ugrEjPfo4/5Q4UbnpoGA==", 133 | "dev": true, 134 | "dependencies": { 135 | "@miniflare/core": "2.6.0", 136 | "@miniflare/shared": "2.6.0", 137 | "@miniflare/web-sockets": "2.6.0", 138 | "kleur": "^4.1.4", 139 | "selfsigned": "^2.0.0", 140 | "undici": "5.5.1", 141 | "ws": "^8.2.2", 142 | "youch": "^2.2.2" 143 | }, 144 | "engines": { 145 | "node": ">=16.7" 146 | } 147 | }, 148 | "node_modules/@miniflare/kv": { 149 | "version": "2.6.0", 150 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.6.0.tgz", 151 | "integrity": "sha512-7Q+Q0Wwinsz85qpKLlBeXSCLweiVowpMJ5AmQpmELnTya59HQ24cOUHxPd64hXFhdYXVIxOmk6lQaZ21JhdHGQ==", 152 | "dev": true, 153 | "dependencies": { 154 | "@miniflare/shared": "2.6.0" 155 | }, 156 | "engines": { 157 | "node": ">=16.7" 158 | } 159 | }, 160 | "node_modules/@miniflare/r2": { 161 | "version": "2.6.0", 162 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.6.0.tgz", 163 | "integrity": "sha512-Ymbqu17ajtuk9b11txF2h1Ewqqlu3XCCpAwAgCQa6AK1yRidQECCPq9w9oXZxE1p5aaSuLTOUbgSdtveFCsLxQ==", 164 | "dev": true, 165 | "dependencies": { 166 | "@miniflare/shared": "2.6.0", 167 | "undici": "5.5.1" 168 | }, 169 | "engines": { 170 | "node": ">=16.7" 171 | } 172 | }, 173 | "node_modules/@miniflare/runner-vm": { 174 | "version": "2.6.0", 175 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.6.0.tgz", 176 | "integrity": "sha512-ZxsiVMMUcjb01LwrO2t50YbU5PT5s3k7DrmR5185R/n04K5BikqZz8eQf8lKlQQYem0BROqmmQgurZGw0a2HUw==", 177 | "dev": true, 178 | "dependencies": { 179 | "@miniflare/shared": "2.6.0" 180 | }, 181 | "engines": { 182 | "node": ">=16.7" 183 | } 184 | }, 185 | "node_modules/@miniflare/scheduler": { 186 | "version": "2.6.0", 187 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.6.0.tgz", 188 | "integrity": "sha512-BM+RDF+8twkTCOb7Oz0NIs5phzAVJ/Gx7tFZR23fGsZjWRnE3TBeqfzaNutU9pcoWDZtBQqEJMeTeb0KZTo75Q==", 189 | "dev": true, 190 | "dependencies": { 191 | "@miniflare/core": "2.6.0", 192 | "@miniflare/shared": "2.6.0", 193 | "cron-schedule": "^3.0.4" 194 | }, 195 | "engines": { 196 | "node": ">=16.7" 197 | } 198 | }, 199 | "node_modules/@miniflare/shared": { 200 | "version": "2.6.0", 201 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.6.0.tgz", 202 | "integrity": "sha512-/7k4C37GF0INu99LNFmFhHYL6U9/oRY/nWDa5sr6+lPEKKm2rkmfvDIA+YNAj7Ql61ZWMgEMj0S3NhV0rWkj7Q==", 203 | "dev": true, 204 | "dependencies": { 205 | "ignore": "^5.1.8", 206 | "kleur": "^4.1.4" 207 | }, 208 | "engines": { 209 | "node": ">=16.7" 210 | } 211 | }, 212 | "node_modules/@miniflare/sites": { 213 | "version": "2.6.0", 214 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.6.0.tgz", 215 | "integrity": "sha512-XfWhpREC638LOGNmuHaPn1MAz1sh2mz+VdMsjRCzUo6NwPl4IcUhnorJR62Xr0qmI/RqVMTZbvzrChXio4Bi4A==", 216 | "dev": true, 217 | "dependencies": { 218 | "@miniflare/kv": "2.6.0", 219 | "@miniflare/shared": "2.6.0", 220 | "@miniflare/storage-file": "2.6.0" 221 | }, 222 | "engines": { 223 | "node": ">=16.7" 224 | } 225 | }, 226 | "node_modules/@miniflare/storage-file": { 227 | "version": "2.6.0", 228 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.6.0.tgz", 229 | "integrity": "sha512-xprDVJClQ2X1vXVPM16WQZz3rS+6fNuCYC8bfEFHABDByQoUNDpk8q+m1IpTaFXYivYxRhE+xr7eK2QQP068tA==", 230 | "dev": true, 231 | "dependencies": { 232 | "@miniflare/shared": "2.6.0", 233 | "@miniflare/storage-memory": "2.6.0" 234 | }, 235 | "engines": { 236 | "node": ">=16.7" 237 | } 238 | }, 239 | "node_modules/@miniflare/storage-memory": { 240 | "version": "2.6.0", 241 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.6.0.tgz", 242 | "integrity": "sha512-0EwELTG2r6IC4AMlQv0YXRZdw9g/lCydceuGKeFkWAVb55pY+yMBxkJO9VV7QOrEx8MLsR8tsfl5SBK3AkfLtA==", 243 | "dev": true, 244 | "dependencies": { 245 | "@miniflare/shared": "2.6.0" 246 | }, 247 | "engines": { 248 | "node": ">=16.7" 249 | } 250 | }, 251 | "node_modules/@miniflare/watcher": { 252 | "version": "2.6.0", 253 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.6.0.tgz", 254 | "integrity": "sha512-mttfhNDmEIFo2rWF73JeWj1TLN+3cQC1TFhbtLApz9bXilLywArXMYqDJGA8PUnJCFM/8k2FDjaFNiPy6ggIJw==", 255 | "dev": true, 256 | "dependencies": { 257 | "@miniflare/shared": "2.6.0" 258 | }, 259 | "engines": { 260 | "node": ">=16.7" 261 | } 262 | }, 263 | "node_modules/@miniflare/web-sockets": { 264 | "version": "2.6.0", 265 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.6.0.tgz", 266 | "integrity": "sha512-ePbcuP9LrStVTllZzqx2oNVoOpceyU3jJF3nGDMNW5+bqB+BdeTggSF8rhER7omcSCswCMY2Do6VelIcAXHkXA==", 267 | "dev": true, 268 | "dependencies": { 269 | "@miniflare/core": "2.6.0", 270 | "@miniflare/shared": "2.6.0", 271 | "undici": "5.5.1", 272 | "ws": "^8.2.2" 273 | }, 274 | "engines": { 275 | "node": ">=16.7" 276 | } 277 | }, 278 | "node_modules/@types/stack-trace": { 279 | "version": "0.0.29", 280 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 281 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 282 | "dev": true 283 | }, 284 | "node_modules/anymatch": { 285 | "version": "3.1.2", 286 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 287 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 288 | "dev": true, 289 | "dependencies": { 290 | "normalize-path": "^3.0.0", 291 | "picomatch": "^2.0.4" 292 | }, 293 | "engines": { 294 | "node": ">= 8" 295 | } 296 | }, 297 | "node_modules/binary-extensions": { 298 | "version": "2.2.0", 299 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 300 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 301 | "dev": true, 302 | "engines": { 303 | "node": ">=8" 304 | } 305 | }, 306 | "node_modules/blake3-wasm": { 307 | "version": "2.1.5", 308 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 309 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 310 | "dev": true 311 | }, 312 | "node_modules/braces": { 313 | "version": "3.0.2", 314 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 315 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 316 | "dev": true, 317 | "dependencies": { 318 | "fill-range": "^7.0.1" 319 | }, 320 | "engines": { 321 | "node": ">=8" 322 | } 323 | }, 324 | "node_modules/buffer-from": { 325 | "version": "1.1.2", 326 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 327 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 328 | "dev": true 329 | }, 330 | "node_modules/busboy": { 331 | "version": "1.6.0", 332 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 333 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 334 | "dev": true, 335 | "dependencies": { 336 | "streamsearch": "^1.1.0" 337 | }, 338 | "engines": { 339 | "node": ">=10.16.0" 340 | } 341 | }, 342 | "node_modules/chokidar": { 343 | "version": "3.5.3", 344 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 345 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 346 | "dev": true, 347 | "funding": [ 348 | { 349 | "type": "individual", 350 | "url": "https://paulmillr.com/funding/" 351 | } 352 | ], 353 | "dependencies": { 354 | "anymatch": "~3.1.2", 355 | "braces": "~3.0.2", 356 | "glob-parent": "~5.1.2", 357 | "is-binary-path": "~2.1.0", 358 | "is-glob": "~4.0.1", 359 | "normalize-path": "~3.0.0", 360 | "readdirp": "~3.6.0" 361 | }, 362 | "engines": { 363 | "node": ">= 8.10.0" 364 | }, 365 | "optionalDependencies": { 366 | "fsevents": "~2.3.2" 367 | } 368 | }, 369 | "node_modules/cookie": { 370 | "version": "0.4.2", 371 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 372 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 373 | "dev": true, 374 | "engines": { 375 | "node": ">= 0.6" 376 | } 377 | }, 378 | "node_modules/cron-schedule": { 379 | "version": "3.0.6", 380 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 381 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 382 | "dev": true 383 | }, 384 | "node_modules/dotenv": { 385 | "version": "10.0.0", 386 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 387 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 388 | "dev": true, 389 | "engines": { 390 | "node": ">=10" 391 | } 392 | }, 393 | "node_modules/esbuild": { 394 | "version": "0.14.47", 395 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.47.tgz", 396 | "integrity": "sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==", 397 | "dev": true, 398 | "hasInstallScript": true, 399 | "bin": { 400 | "esbuild": "bin/esbuild" 401 | }, 402 | "engines": { 403 | "node": ">=12" 404 | }, 405 | "optionalDependencies": { 406 | "esbuild-android-64": "0.14.47", 407 | "esbuild-android-arm64": "0.14.47", 408 | "esbuild-darwin-64": "0.14.47", 409 | "esbuild-darwin-arm64": "0.14.47", 410 | "esbuild-freebsd-64": "0.14.47", 411 | "esbuild-freebsd-arm64": "0.14.47", 412 | "esbuild-linux-32": "0.14.47", 413 | "esbuild-linux-64": "0.14.47", 414 | "esbuild-linux-arm": "0.14.47", 415 | "esbuild-linux-arm64": "0.14.47", 416 | "esbuild-linux-mips64le": "0.14.47", 417 | "esbuild-linux-ppc64le": "0.14.47", 418 | "esbuild-linux-riscv64": "0.14.47", 419 | "esbuild-linux-s390x": "0.14.47", 420 | "esbuild-netbsd-64": "0.14.47", 421 | "esbuild-openbsd-64": "0.14.47", 422 | "esbuild-sunos-64": "0.14.47", 423 | "esbuild-windows-32": "0.14.47", 424 | "esbuild-windows-64": "0.14.47", 425 | "esbuild-windows-arm64": "0.14.47" 426 | } 427 | }, 428 | "node_modules/esbuild-android-64": { 429 | "version": "0.14.47", 430 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz", 431 | "integrity": "sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==", 432 | "cpu": [ 433 | "x64" 434 | ], 435 | "dev": true, 436 | "optional": true, 437 | "os": [ 438 | "android" 439 | ], 440 | "engines": { 441 | "node": ">=12" 442 | } 443 | }, 444 | "node_modules/esbuild-android-arm64": { 445 | "version": "0.14.47", 446 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz", 447 | "integrity": "sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==", 448 | "cpu": [ 449 | "arm64" 450 | ], 451 | "dev": true, 452 | "optional": true, 453 | "os": [ 454 | "android" 455 | ], 456 | "engines": { 457 | "node": ">=12" 458 | } 459 | }, 460 | "node_modules/esbuild-darwin-64": { 461 | "version": "0.14.47", 462 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz", 463 | "integrity": "sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==", 464 | "cpu": [ 465 | "x64" 466 | ], 467 | "dev": true, 468 | "optional": true, 469 | "os": [ 470 | "darwin" 471 | ], 472 | "engines": { 473 | "node": ">=12" 474 | } 475 | }, 476 | "node_modules/esbuild-darwin-arm64": { 477 | "version": "0.14.47", 478 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz", 479 | "integrity": "sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==", 480 | "cpu": [ 481 | "arm64" 482 | ], 483 | "dev": true, 484 | "optional": true, 485 | "os": [ 486 | "darwin" 487 | ], 488 | "engines": { 489 | "node": ">=12" 490 | } 491 | }, 492 | "node_modules/esbuild-freebsd-64": { 493 | "version": "0.14.47", 494 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz", 495 | "integrity": "sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==", 496 | "cpu": [ 497 | "x64" 498 | ], 499 | "dev": true, 500 | "optional": true, 501 | "os": [ 502 | "freebsd" 503 | ], 504 | "engines": { 505 | "node": ">=12" 506 | } 507 | }, 508 | "node_modules/esbuild-freebsd-arm64": { 509 | "version": "0.14.47", 510 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz", 511 | "integrity": "sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==", 512 | "cpu": [ 513 | "arm64" 514 | ], 515 | "dev": true, 516 | "optional": true, 517 | "os": [ 518 | "freebsd" 519 | ], 520 | "engines": { 521 | "node": ">=12" 522 | } 523 | }, 524 | "node_modules/esbuild-linux-32": { 525 | "version": "0.14.47", 526 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz", 527 | "integrity": "sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==", 528 | "cpu": [ 529 | "ia32" 530 | ], 531 | "dev": true, 532 | "optional": true, 533 | "os": [ 534 | "linux" 535 | ], 536 | "engines": { 537 | "node": ">=12" 538 | } 539 | }, 540 | "node_modules/esbuild-linux-64": { 541 | "version": "0.14.47", 542 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz", 543 | "integrity": "sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==", 544 | "cpu": [ 545 | "x64" 546 | ], 547 | "dev": true, 548 | "optional": true, 549 | "os": [ 550 | "linux" 551 | ], 552 | "engines": { 553 | "node": ">=12" 554 | } 555 | }, 556 | "node_modules/esbuild-linux-arm": { 557 | "version": "0.14.47", 558 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz", 559 | "integrity": "sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==", 560 | "cpu": [ 561 | "arm" 562 | ], 563 | "dev": true, 564 | "optional": true, 565 | "os": [ 566 | "linux" 567 | ], 568 | "engines": { 569 | "node": ">=12" 570 | } 571 | }, 572 | "node_modules/esbuild-linux-arm64": { 573 | "version": "0.14.47", 574 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz", 575 | "integrity": "sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==", 576 | "cpu": [ 577 | "arm64" 578 | ], 579 | "dev": true, 580 | "optional": true, 581 | "os": [ 582 | "linux" 583 | ], 584 | "engines": { 585 | "node": ">=12" 586 | } 587 | }, 588 | "node_modules/esbuild-linux-mips64le": { 589 | "version": "0.14.47", 590 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz", 591 | "integrity": "sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==", 592 | "cpu": [ 593 | "mips64el" 594 | ], 595 | "dev": true, 596 | "optional": true, 597 | "os": [ 598 | "linux" 599 | ], 600 | "engines": { 601 | "node": ">=12" 602 | } 603 | }, 604 | "node_modules/esbuild-linux-ppc64le": { 605 | "version": "0.14.47", 606 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz", 607 | "integrity": "sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==", 608 | "cpu": [ 609 | "ppc64" 610 | ], 611 | "dev": true, 612 | "optional": true, 613 | "os": [ 614 | "linux" 615 | ], 616 | "engines": { 617 | "node": ">=12" 618 | } 619 | }, 620 | "node_modules/esbuild-linux-riscv64": { 621 | "version": "0.14.47", 622 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz", 623 | "integrity": "sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==", 624 | "cpu": [ 625 | "riscv64" 626 | ], 627 | "dev": true, 628 | "optional": true, 629 | "os": [ 630 | "linux" 631 | ], 632 | "engines": { 633 | "node": ">=12" 634 | } 635 | }, 636 | "node_modules/esbuild-linux-s390x": { 637 | "version": "0.14.47", 638 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz", 639 | "integrity": "sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==", 640 | "cpu": [ 641 | "s390x" 642 | ], 643 | "dev": true, 644 | "optional": true, 645 | "os": [ 646 | "linux" 647 | ], 648 | "engines": { 649 | "node": ">=12" 650 | } 651 | }, 652 | "node_modules/esbuild-netbsd-64": { 653 | "version": "0.14.47", 654 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz", 655 | "integrity": "sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==", 656 | "cpu": [ 657 | "x64" 658 | ], 659 | "dev": true, 660 | "optional": true, 661 | "os": [ 662 | "netbsd" 663 | ], 664 | "engines": { 665 | "node": ">=12" 666 | } 667 | }, 668 | "node_modules/esbuild-openbsd-64": { 669 | "version": "0.14.47", 670 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz", 671 | "integrity": "sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==", 672 | "cpu": [ 673 | "x64" 674 | ], 675 | "dev": true, 676 | "optional": true, 677 | "os": [ 678 | "openbsd" 679 | ], 680 | "engines": { 681 | "node": ">=12" 682 | } 683 | }, 684 | "node_modules/esbuild-sunos-64": { 685 | "version": "0.14.47", 686 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz", 687 | "integrity": "sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==", 688 | "cpu": [ 689 | "x64" 690 | ], 691 | "dev": true, 692 | "optional": true, 693 | "os": [ 694 | "sunos" 695 | ], 696 | "engines": { 697 | "node": ">=12" 698 | } 699 | }, 700 | "node_modules/esbuild-windows-32": { 701 | "version": "0.14.47", 702 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz", 703 | "integrity": "sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==", 704 | "cpu": [ 705 | "ia32" 706 | ], 707 | "dev": true, 708 | "optional": true, 709 | "os": [ 710 | "win32" 711 | ], 712 | "engines": { 713 | "node": ">=12" 714 | } 715 | }, 716 | "node_modules/esbuild-windows-64": { 717 | "version": "0.14.47", 718 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz", 719 | "integrity": "sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==", 720 | "cpu": [ 721 | "x64" 722 | ], 723 | "dev": true, 724 | "optional": true, 725 | "os": [ 726 | "win32" 727 | ], 728 | "engines": { 729 | "node": ">=12" 730 | } 731 | }, 732 | "node_modules/esbuild-windows-arm64": { 733 | "version": "0.14.47", 734 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz", 735 | "integrity": "sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==", 736 | "cpu": [ 737 | "arm64" 738 | ], 739 | "dev": true, 740 | "optional": true, 741 | "os": [ 742 | "win32" 743 | ], 744 | "engines": { 745 | "node": ">=12" 746 | } 747 | }, 748 | "node_modules/escape-string-regexp": { 749 | "version": "4.0.0", 750 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 751 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 752 | "dev": true, 753 | "engines": { 754 | "node": ">=10" 755 | }, 756 | "funding": { 757 | "url": "https://github.com/sponsors/sindresorhus" 758 | } 759 | }, 760 | "node_modules/estree-walker": { 761 | "version": "0.6.1", 762 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 763 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 764 | "dev": true 765 | }, 766 | "node_modules/fill-range": { 767 | "version": "7.0.1", 768 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 769 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 770 | "dev": true, 771 | "dependencies": { 772 | "to-regex-range": "^5.0.1" 773 | }, 774 | "engines": { 775 | "node": ">=8" 776 | } 777 | }, 778 | "node_modules/fsevents": { 779 | "version": "2.3.2", 780 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 781 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 782 | "dev": true, 783 | "hasInstallScript": true, 784 | "optional": true, 785 | "os": [ 786 | "darwin" 787 | ], 788 | "engines": { 789 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 790 | } 791 | }, 792 | "node_modules/glob-parent": { 793 | "version": "5.1.2", 794 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 795 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 796 | "dev": true, 797 | "dependencies": { 798 | "is-glob": "^4.0.1" 799 | }, 800 | "engines": { 801 | "node": ">= 6" 802 | } 803 | }, 804 | "node_modules/html-rewriter-wasm": { 805 | "version": "0.4.1", 806 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 807 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 808 | "dev": true 809 | }, 810 | "node_modules/http-cache-semantics": { 811 | "version": "4.1.0", 812 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 813 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", 814 | "dev": true 815 | }, 816 | "node_modules/ignore": { 817 | "version": "5.2.0", 818 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 819 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 820 | "dev": true, 821 | "engines": { 822 | "node": ">= 4" 823 | } 824 | }, 825 | "node_modules/is-binary-path": { 826 | "version": "2.1.0", 827 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 828 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 829 | "dev": true, 830 | "dependencies": { 831 | "binary-extensions": "^2.0.0" 832 | }, 833 | "engines": { 834 | "node": ">=8" 835 | } 836 | }, 837 | "node_modules/is-extglob": { 838 | "version": "2.1.1", 839 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 840 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 841 | "dev": true, 842 | "engines": { 843 | "node": ">=0.10.0" 844 | } 845 | }, 846 | "node_modules/is-glob": { 847 | "version": "4.0.3", 848 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 849 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 850 | "dev": true, 851 | "dependencies": { 852 | "is-extglob": "^2.1.1" 853 | }, 854 | "engines": { 855 | "node": ">=0.10.0" 856 | } 857 | }, 858 | "node_modules/is-number": { 859 | "version": "7.0.0", 860 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 861 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 862 | "dev": true, 863 | "engines": { 864 | "node": ">=0.12.0" 865 | } 866 | }, 867 | "node_modules/kleur": { 868 | "version": "4.1.5", 869 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 870 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 871 | "dev": true, 872 | "engines": { 873 | "node": ">=6" 874 | } 875 | }, 876 | "node_modules/magic-string": { 877 | "version": "0.25.9", 878 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", 879 | "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", 880 | "dev": true, 881 | "dependencies": { 882 | "sourcemap-codec": "^1.4.8" 883 | } 884 | }, 885 | "node_modules/mime": { 886 | "version": "3.0.0", 887 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 888 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 889 | "dev": true, 890 | "bin": { 891 | "mime": "cli.js" 892 | }, 893 | "engines": { 894 | "node": ">=10.0.0" 895 | } 896 | }, 897 | "node_modules/miniflare": { 898 | "version": "2.6.0", 899 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.6.0.tgz", 900 | "integrity": "sha512-KDAQZV2aDZ044X1ihlCIa6DPdq1w3fUJFW4xZ+r+DPUxj9t1AuehjR9Fc6zCmZQrk12gLXDSZSyNft1ozm1X7Q==", 901 | "dev": true, 902 | "dependencies": { 903 | "@miniflare/cache": "2.6.0", 904 | "@miniflare/cli-parser": "2.6.0", 905 | "@miniflare/core": "2.6.0", 906 | "@miniflare/durable-objects": "2.6.0", 907 | "@miniflare/html-rewriter": "2.6.0", 908 | "@miniflare/http-server": "2.6.0", 909 | "@miniflare/kv": "2.6.0", 910 | "@miniflare/r2": "2.6.0", 911 | "@miniflare/runner-vm": "2.6.0", 912 | "@miniflare/scheduler": "2.6.0", 913 | "@miniflare/shared": "2.6.0", 914 | "@miniflare/sites": "2.6.0", 915 | "@miniflare/storage-file": "2.6.0", 916 | "@miniflare/storage-memory": "2.6.0", 917 | "@miniflare/web-sockets": "2.6.0", 918 | "kleur": "^4.1.4", 919 | "semiver": "^1.1.0", 920 | "source-map-support": "^0.5.20", 921 | "undici": "5.5.1" 922 | }, 923 | "bin": { 924 | "miniflare": "bootstrap.js" 925 | }, 926 | "engines": { 927 | "node": ">=16.7" 928 | }, 929 | "peerDependencies": { 930 | "@miniflare/storage-redis": "2.6.0", 931 | "cron-schedule": "^3.0.4", 932 | "ioredis": "^4.27.9" 933 | }, 934 | "peerDependenciesMeta": { 935 | "@miniflare/storage-redis": { 936 | "optional": true 937 | }, 938 | "cron-schedule": { 939 | "optional": true 940 | }, 941 | "ioredis": { 942 | "optional": true 943 | } 944 | } 945 | }, 946 | "node_modules/mustache": { 947 | "version": "4.2.0", 948 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 949 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 950 | "dev": true, 951 | "bin": { 952 | "mustache": "bin/mustache" 953 | } 954 | }, 955 | "node_modules/nanoid": { 956 | "version": "3.3.4", 957 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 958 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 959 | "dev": true, 960 | "bin": { 961 | "nanoid": "bin/nanoid.cjs" 962 | }, 963 | "engines": { 964 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 965 | } 966 | }, 967 | "node_modules/node-forge": { 968 | "version": "1.3.1", 969 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 970 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 971 | "dev": true, 972 | "engines": { 973 | "node": ">= 6.13.0" 974 | } 975 | }, 976 | "node_modules/normalize-path": { 977 | "version": "3.0.0", 978 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 979 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 980 | "dev": true, 981 | "engines": { 982 | "node": ">=0.10.0" 983 | } 984 | }, 985 | "node_modules/path-to-regexp": { 986 | "version": "6.2.1", 987 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", 988 | "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", 989 | "dev": true 990 | }, 991 | "node_modules/picomatch": { 992 | "version": "2.3.1", 993 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 994 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 995 | "dev": true, 996 | "engines": { 997 | "node": ">=8.6" 998 | }, 999 | "funding": { 1000 | "url": "https://github.com/sponsors/jonschlinkert" 1001 | } 1002 | }, 1003 | "node_modules/readdirp": { 1004 | "version": "3.6.0", 1005 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1006 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1007 | "dev": true, 1008 | "dependencies": { 1009 | "picomatch": "^2.2.1" 1010 | }, 1011 | "engines": { 1012 | "node": ">=8.10.0" 1013 | } 1014 | }, 1015 | "node_modules/rollup-plugin-inject": { 1016 | "version": "3.0.2", 1017 | "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", 1018 | "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", 1019 | "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", 1020 | "dev": true, 1021 | "dependencies": { 1022 | "estree-walker": "^0.6.1", 1023 | "magic-string": "^0.25.3", 1024 | "rollup-pluginutils": "^2.8.1" 1025 | } 1026 | }, 1027 | "node_modules/rollup-plugin-node-polyfills": { 1028 | "version": "0.2.1", 1029 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", 1030 | "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", 1031 | "dev": true, 1032 | "dependencies": { 1033 | "rollup-plugin-inject": "^3.0.0" 1034 | } 1035 | }, 1036 | "node_modules/rollup-pluginutils": { 1037 | "version": "2.8.2", 1038 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 1039 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 1040 | "dev": true, 1041 | "dependencies": { 1042 | "estree-walker": "^0.6.1" 1043 | } 1044 | }, 1045 | "node_modules/selfsigned": { 1046 | "version": "2.0.1", 1047 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", 1048 | "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", 1049 | "dev": true, 1050 | "dependencies": { 1051 | "node-forge": "^1" 1052 | }, 1053 | "engines": { 1054 | "node": ">=10" 1055 | } 1056 | }, 1057 | "node_modules/semiver": { 1058 | "version": "1.1.0", 1059 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1060 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1061 | "dev": true, 1062 | "engines": { 1063 | "node": ">=6" 1064 | } 1065 | }, 1066 | "node_modules/set-cookie-parser": { 1067 | "version": "2.5.1", 1068 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 1069 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", 1070 | "dev": true 1071 | }, 1072 | "node_modules/source-map": { 1073 | "version": "0.6.1", 1074 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1075 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1076 | "dev": true, 1077 | "engines": { 1078 | "node": ">=0.10.0" 1079 | } 1080 | }, 1081 | "node_modules/source-map-support": { 1082 | "version": "0.5.21", 1083 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1084 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1085 | "dev": true, 1086 | "dependencies": { 1087 | "buffer-from": "^1.0.0", 1088 | "source-map": "^0.6.0" 1089 | } 1090 | }, 1091 | "node_modules/sourcemap-codec": { 1092 | "version": "1.4.8", 1093 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1094 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1095 | "dev": true 1096 | }, 1097 | "node_modules/stack-trace": { 1098 | "version": "0.0.10", 1099 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1100 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1101 | "dev": true, 1102 | "engines": { 1103 | "node": "*" 1104 | } 1105 | }, 1106 | "node_modules/streamsearch": { 1107 | "version": "1.1.0", 1108 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1109 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1110 | "dev": true, 1111 | "engines": { 1112 | "node": ">=10.0.0" 1113 | } 1114 | }, 1115 | "node_modules/to-regex-range": { 1116 | "version": "5.0.1", 1117 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1118 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1119 | "dev": true, 1120 | "dependencies": { 1121 | "is-number": "^7.0.0" 1122 | }, 1123 | "engines": { 1124 | "node": ">=8.0" 1125 | } 1126 | }, 1127 | "node_modules/undici": { 1128 | "version": "5.5.1", 1129 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", 1130 | "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==", 1131 | "dev": true, 1132 | "engines": { 1133 | "node": ">=12.18" 1134 | } 1135 | }, 1136 | "node_modules/urlpattern-polyfill": { 1137 | "version": "4.0.3", 1138 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1139 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1140 | "dev": true 1141 | }, 1142 | "node_modules/wrangler": { 1143 | "version": "2.0.23", 1144 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.0.23.tgz", 1145 | "integrity": "sha512-qMyK/pmHIrubxuJuXnCwcidY4LlQImcTTyOGoqMJtNz8y+pDfsluExSBpwqGSl3JPUQF2FiofqaBkj/iB8rvYw==", 1146 | "dev": true, 1147 | "dependencies": { 1148 | "@cloudflare/kv-asset-handler": "^0.2.0", 1149 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1150 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1151 | "blake3-wasm": "^2.1.5", 1152 | "chokidar": "^3.5.3", 1153 | "esbuild": "0.14.47", 1154 | "miniflare": "^2.6.0", 1155 | "nanoid": "^3.3.3", 1156 | "path-to-regexp": "^6.2.0", 1157 | "selfsigned": "^2.0.1", 1158 | "xxhash-wasm": "^1.0.1" 1159 | }, 1160 | "bin": { 1161 | "wrangler": "bin/wrangler.js", 1162 | "wrangler2": "bin/wrangler.js" 1163 | }, 1164 | "engines": { 1165 | "node": ">=16.7.0" 1166 | }, 1167 | "optionalDependencies": { 1168 | "fsevents": "~2.3.2" 1169 | } 1170 | }, 1171 | "node_modules/ws": { 1172 | "version": "8.8.1", 1173 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1174 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1175 | "dev": true, 1176 | "engines": { 1177 | "node": ">=10.0.0" 1178 | }, 1179 | "peerDependencies": { 1180 | "bufferutil": "^4.0.1", 1181 | "utf-8-validate": "^5.0.2" 1182 | }, 1183 | "peerDependenciesMeta": { 1184 | "bufferutil": { 1185 | "optional": true 1186 | }, 1187 | "utf-8-validate": { 1188 | "optional": true 1189 | } 1190 | } 1191 | }, 1192 | "node_modules/xxhash-wasm": { 1193 | "version": "1.0.1", 1194 | "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.1.tgz", 1195 | "integrity": "sha512-Lc9CTvDrH2vRoiaUzz25q7lRaviMhz90pkx6YxR9EPYtF99yOJnv2cB+CQ0hp/TLoqrUsk8z/W2EN31T568Azw==", 1196 | "dev": true 1197 | }, 1198 | "node_modules/youch": { 1199 | "version": "2.2.2", 1200 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 1201 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 1202 | "dev": true, 1203 | "dependencies": { 1204 | "@types/stack-trace": "0.0.29", 1205 | "cookie": "^0.4.1", 1206 | "mustache": "^4.2.0", 1207 | "stack-trace": "0.0.10" 1208 | } 1209 | } 1210 | }, 1211 | "dependencies": { 1212 | "@cloudflare/kv-asset-handler": { 1213 | "version": "0.2.0", 1214 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", 1215 | "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", 1216 | "dev": true, 1217 | "requires": { 1218 | "mime": "^3.0.0" 1219 | } 1220 | }, 1221 | "@esbuild-plugins/node-globals-polyfill": { 1222 | "version": "0.1.1", 1223 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", 1224 | "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", 1225 | "dev": true, 1226 | "requires": {} 1227 | }, 1228 | "@esbuild-plugins/node-modules-polyfill": { 1229 | "version": "0.1.4", 1230 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", 1231 | "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", 1232 | "dev": true, 1233 | "requires": { 1234 | "escape-string-regexp": "^4.0.0", 1235 | "rollup-plugin-node-polyfills": "^0.2.1" 1236 | } 1237 | }, 1238 | "@iarna/toml": { 1239 | "version": "2.2.5", 1240 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 1241 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 1242 | "dev": true 1243 | }, 1244 | "@miniflare/cache": { 1245 | "version": "2.6.0", 1246 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.6.0.tgz", 1247 | "integrity": "sha512-4oh8MgpquoxaslI7Z8sMzmEZR0Dc+L3aEh69o9d8ZCs4nUdOENnfKlY50O5nEnL7nhhyAljkMBaXD2wAH2DLeQ==", 1248 | "dev": true, 1249 | "requires": { 1250 | "@miniflare/core": "2.6.0", 1251 | "@miniflare/shared": "2.6.0", 1252 | "http-cache-semantics": "^4.1.0", 1253 | "undici": "5.5.1" 1254 | } 1255 | }, 1256 | "@miniflare/cli-parser": { 1257 | "version": "2.6.0", 1258 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.6.0.tgz", 1259 | "integrity": "sha512-dJDoIPAUqWhzvBHHyqyhobdzDedBYRWZ4yItBi9m4MTU/EneLJ5jryB340SwUnmtBMZxUh/LWdAuUEkKpdVNyA==", 1260 | "dev": true, 1261 | "requires": { 1262 | "@miniflare/shared": "2.6.0", 1263 | "kleur": "^4.1.4" 1264 | } 1265 | }, 1266 | "@miniflare/core": { 1267 | "version": "2.6.0", 1268 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.6.0.tgz", 1269 | "integrity": "sha512-CmofhIRot++GI7NHPMwzNb65+0hWLN186L91BrH/doPVHnT/itmEfzYQpL9bFLD0c/i14dfv+IUNetDdGEBIrw==", 1270 | "dev": true, 1271 | "requires": { 1272 | "@iarna/toml": "^2.2.5", 1273 | "@miniflare/shared": "2.6.0", 1274 | "@miniflare/watcher": "2.6.0", 1275 | "busboy": "^1.6.0", 1276 | "dotenv": "^10.0.0", 1277 | "kleur": "^4.1.4", 1278 | "set-cookie-parser": "^2.4.8", 1279 | "undici": "5.5.1", 1280 | "urlpattern-polyfill": "^4.0.3" 1281 | } 1282 | }, 1283 | "@miniflare/durable-objects": { 1284 | "version": "2.6.0", 1285 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.6.0.tgz", 1286 | "integrity": "sha512-uzWoGFtkIIh3m3HAzqd5f86nOSC0xFli6dq2q7ilE3UjgouOcLqObxJyE/IzvSwsj4DUWFv6//YDfHihK2fGAA==", 1287 | "dev": true, 1288 | "requires": { 1289 | "@miniflare/core": "2.6.0", 1290 | "@miniflare/shared": "2.6.0", 1291 | "@miniflare/storage-memory": "2.6.0", 1292 | "undici": "5.5.1" 1293 | } 1294 | }, 1295 | "@miniflare/html-rewriter": { 1296 | "version": "2.6.0", 1297 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.6.0.tgz", 1298 | "integrity": "sha512-+JqFlIDLzstb/Spj+j/kI6uHzolrqjsMks3Tf24Q4YFo9YYdZguqUFcDz2yr79ZTP/SKXaZH+AYqosnJps4dHQ==", 1299 | "dev": true, 1300 | "requires": { 1301 | "@miniflare/core": "2.6.0", 1302 | "@miniflare/shared": "2.6.0", 1303 | "html-rewriter-wasm": "^0.4.1", 1304 | "undici": "5.5.1" 1305 | } 1306 | }, 1307 | "@miniflare/http-server": { 1308 | "version": "2.6.0", 1309 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.6.0.tgz", 1310 | "integrity": "sha512-FhcAVIpipMEzMCsJBc/b0JhNEJ66GPX60vA2NcqjGKHYbwoPCPlwCFQq2giPzW/R95ugrEjPfo4/5Q4UbnpoGA==", 1311 | "dev": true, 1312 | "requires": { 1313 | "@miniflare/core": "2.6.0", 1314 | "@miniflare/shared": "2.6.0", 1315 | "@miniflare/web-sockets": "2.6.0", 1316 | "kleur": "^4.1.4", 1317 | "selfsigned": "^2.0.0", 1318 | "undici": "5.5.1", 1319 | "ws": "^8.2.2", 1320 | "youch": "^2.2.2" 1321 | } 1322 | }, 1323 | "@miniflare/kv": { 1324 | "version": "2.6.0", 1325 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.6.0.tgz", 1326 | "integrity": "sha512-7Q+Q0Wwinsz85qpKLlBeXSCLweiVowpMJ5AmQpmELnTya59HQ24cOUHxPd64hXFhdYXVIxOmk6lQaZ21JhdHGQ==", 1327 | "dev": true, 1328 | "requires": { 1329 | "@miniflare/shared": "2.6.0" 1330 | } 1331 | }, 1332 | "@miniflare/r2": { 1333 | "version": "2.6.0", 1334 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.6.0.tgz", 1335 | "integrity": "sha512-Ymbqu17ajtuk9b11txF2h1Ewqqlu3XCCpAwAgCQa6AK1yRidQECCPq9w9oXZxE1p5aaSuLTOUbgSdtveFCsLxQ==", 1336 | "dev": true, 1337 | "requires": { 1338 | "@miniflare/shared": "2.6.0", 1339 | "undici": "5.5.1" 1340 | } 1341 | }, 1342 | "@miniflare/runner-vm": { 1343 | "version": "2.6.0", 1344 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.6.0.tgz", 1345 | "integrity": "sha512-ZxsiVMMUcjb01LwrO2t50YbU5PT5s3k7DrmR5185R/n04K5BikqZz8eQf8lKlQQYem0BROqmmQgurZGw0a2HUw==", 1346 | "dev": true, 1347 | "requires": { 1348 | "@miniflare/shared": "2.6.0" 1349 | } 1350 | }, 1351 | "@miniflare/scheduler": { 1352 | "version": "2.6.0", 1353 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.6.0.tgz", 1354 | "integrity": "sha512-BM+RDF+8twkTCOb7Oz0NIs5phzAVJ/Gx7tFZR23fGsZjWRnE3TBeqfzaNutU9pcoWDZtBQqEJMeTeb0KZTo75Q==", 1355 | "dev": true, 1356 | "requires": { 1357 | "@miniflare/core": "2.6.0", 1358 | "@miniflare/shared": "2.6.0", 1359 | "cron-schedule": "^3.0.4" 1360 | } 1361 | }, 1362 | "@miniflare/shared": { 1363 | "version": "2.6.0", 1364 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.6.0.tgz", 1365 | "integrity": "sha512-/7k4C37GF0INu99LNFmFhHYL6U9/oRY/nWDa5sr6+lPEKKm2rkmfvDIA+YNAj7Ql61ZWMgEMj0S3NhV0rWkj7Q==", 1366 | "dev": true, 1367 | "requires": { 1368 | "ignore": "^5.1.8", 1369 | "kleur": "^4.1.4" 1370 | } 1371 | }, 1372 | "@miniflare/sites": { 1373 | "version": "2.6.0", 1374 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.6.0.tgz", 1375 | "integrity": "sha512-XfWhpREC638LOGNmuHaPn1MAz1sh2mz+VdMsjRCzUo6NwPl4IcUhnorJR62Xr0qmI/RqVMTZbvzrChXio4Bi4A==", 1376 | "dev": true, 1377 | "requires": { 1378 | "@miniflare/kv": "2.6.0", 1379 | "@miniflare/shared": "2.6.0", 1380 | "@miniflare/storage-file": "2.6.0" 1381 | } 1382 | }, 1383 | "@miniflare/storage-file": { 1384 | "version": "2.6.0", 1385 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.6.0.tgz", 1386 | "integrity": "sha512-xprDVJClQ2X1vXVPM16WQZz3rS+6fNuCYC8bfEFHABDByQoUNDpk8q+m1IpTaFXYivYxRhE+xr7eK2QQP068tA==", 1387 | "dev": true, 1388 | "requires": { 1389 | "@miniflare/shared": "2.6.0", 1390 | "@miniflare/storage-memory": "2.6.0" 1391 | } 1392 | }, 1393 | "@miniflare/storage-memory": { 1394 | "version": "2.6.0", 1395 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.6.0.tgz", 1396 | "integrity": "sha512-0EwELTG2r6IC4AMlQv0YXRZdw9g/lCydceuGKeFkWAVb55pY+yMBxkJO9VV7QOrEx8MLsR8tsfl5SBK3AkfLtA==", 1397 | "dev": true, 1398 | "requires": { 1399 | "@miniflare/shared": "2.6.0" 1400 | } 1401 | }, 1402 | "@miniflare/watcher": { 1403 | "version": "2.6.0", 1404 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.6.0.tgz", 1405 | "integrity": "sha512-mttfhNDmEIFo2rWF73JeWj1TLN+3cQC1TFhbtLApz9bXilLywArXMYqDJGA8PUnJCFM/8k2FDjaFNiPy6ggIJw==", 1406 | "dev": true, 1407 | "requires": { 1408 | "@miniflare/shared": "2.6.0" 1409 | } 1410 | }, 1411 | "@miniflare/web-sockets": { 1412 | "version": "2.6.0", 1413 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.6.0.tgz", 1414 | "integrity": "sha512-ePbcuP9LrStVTllZzqx2oNVoOpceyU3jJF3nGDMNW5+bqB+BdeTggSF8rhER7omcSCswCMY2Do6VelIcAXHkXA==", 1415 | "dev": true, 1416 | "requires": { 1417 | "@miniflare/core": "2.6.0", 1418 | "@miniflare/shared": "2.6.0", 1419 | "undici": "5.5.1", 1420 | "ws": "^8.2.2" 1421 | } 1422 | }, 1423 | "@types/stack-trace": { 1424 | "version": "0.0.29", 1425 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 1426 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 1427 | "dev": true 1428 | }, 1429 | "anymatch": { 1430 | "version": "3.1.2", 1431 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1432 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1433 | "dev": true, 1434 | "requires": { 1435 | "normalize-path": "^3.0.0", 1436 | "picomatch": "^2.0.4" 1437 | } 1438 | }, 1439 | "binary-extensions": { 1440 | "version": "2.2.0", 1441 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1442 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1443 | "dev": true 1444 | }, 1445 | "blake3-wasm": { 1446 | "version": "2.1.5", 1447 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1448 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1449 | "dev": true 1450 | }, 1451 | "braces": { 1452 | "version": "3.0.2", 1453 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1454 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1455 | "dev": true, 1456 | "requires": { 1457 | "fill-range": "^7.0.1" 1458 | } 1459 | }, 1460 | "buffer-from": { 1461 | "version": "1.1.2", 1462 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1463 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 1464 | "dev": true 1465 | }, 1466 | "busboy": { 1467 | "version": "1.6.0", 1468 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 1469 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 1470 | "dev": true, 1471 | "requires": { 1472 | "streamsearch": "^1.1.0" 1473 | } 1474 | }, 1475 | "chokidar": { 1476 | "version": "3.5.3", 1477 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1478 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1479 | "dev": true, 1480 | "requires": { 1481 | "anymatch": "~3.1.2", 1482 | "braces": "~3.0.2", 1483 | "fsevents": "~2.3.2", 1484 | "glob-parent": "~5.1.2", 1485 | "is-binary-path": "~2.1.0", 1486 | "is-glob": "~4.0.1", 1487 | "normalize-path": "~3.0.0", 1488 | "readdirp": "~3.6.0" 1489 | } 1490 | }, 1491 | "cookie": { 1492 | "version": "0.4.2", 1493 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 1494 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 1495 | "dev": true 1496 | }, 1497 | "cron-schedule": { 1498 | "version": "3.0.6", 1499 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 1500 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 1501 | "dev": true 1502 | }, 1503 | "dotenv": { 1504 | "version": "10.0.0", 1505 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 1506 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 1507 | "dev": true 1508 | }, 1509 | "esbuild": { 1510 | "version": "0.14.47", 1511 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.47.tgz", 1512 | "integrity": "sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==", 1513 | "dev": true, 1514 | "requires": { 1515 | "esbuild-android-64": "0.14.47", 1516 | "esbuild-android-arm64": "0.14.47", 1517 | "esbuild-darwin-64": "0.14.47", 1518 | "esbuild-darwin-arm64": "0.14.47", 1519 | "esbuild-freebsd-64": "0.14.47", 1520 | "esbuild-freebsd-arm64": "0.14.47", 1521 | "esbuild-linux-32": "0.14.47", 1522 | "esbuild-linux-64": "0.14.47", 1523 | "esbuild-linux-arm": "0.14.47", 1524 | "esbuild-linux-arm64": "0.14.47", 1525 | "esbuild-linux-mips64le": "0.14.47", 1526 | "esbuild-linux-ppc64le": "0.14.47", 1527 | "esbuild-linux-riscv64": "0.14.47", 1528 | "esbuild-linux-s390x": "0.14.47", 1529 | "esbuild-netbsd-64": "0.14.47", 1530 | "esbuild-openbsd-64": "0.14.47", 1531 | "esbuild-sunos-64": "0.14.47", 1532 | "esbuild-windows-32": "0.14.47", 1533 | "esbuild-windows-64": "0.14.47", 1534 | "esbuild-windows-arm64": "0.14.47" 1535 | } 1536 | }, 1537 | "esbuild-android-64": { 1538 | "version": "0.14.47", 1539 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz", 1540 | "integrity": "sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==", 1541 | "dev": true, 1542 | "optional": true 1543 | }, 1544 | "esbuild-android-arm64": { 1545 | "version": "0.14.47", 1546 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz", 1547 | "integrity": "sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==", 1548 | "dev": true, 1549 | "optional": true 1550 | }, 1551 | "esbuild-darwin-64": { 1552 | "version": "0.14.47", 1553 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz", 1554 | "integrity": "sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==", 1555 | "dev": true, 1556 | "optional": true 1557 | }, 1558 | "esbuild-darwin-arm64": { 1559 | "version": "0.14.47", 1560 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz", 1561 | "integrity": "sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==", 1562 | "dev": true, 1563 | "optional": true 1564 | }, 1565 | "esbuild-freebsd-64": { 1566 | "version": "0.14.47", 1567 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz", 1568 | "integrity": "sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==", 1569 | "dev": true, 1570 | "optional": true 1571 | }, 1572 | "esbuild-freebsd-arm64": { 1573 | "version": "0.14.47", 1574 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz", 1575 | "integrity": "sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==", 1576 | "dev": true, 1577 | "optional": true 1578 | }, 1579 | "esbuild-linux-32": { 1580 | "version": "0.14.47", 1581 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz", 1582 | "integrity": "sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==", 1583 | "dev": true, 1584 | "optional": true 1585 | }, 1586 | "esbuild-linux-64": { 1587 | "version": "0.14.47", 1588 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz", 1589 | "integrity": "sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==", 1590 | "dev": true, 1591 | "optional": true 1592 | }, 1593 | "esbuild-linux-arm": { 1594 | "version": "0.14.47", 1595 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz", 1596 | "integrity": "sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==", 1597 | "dev": true, 1598 | "optional": true 1599 | }, 1600 | "esbuild-linux-arm64": { 1601 | "version": "0.14.47", 1602 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz", 1603 | "integrity": "sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==", 1604 | "dev": true, 1605 | "optional": true 1606 | }, 1607 | "esbuild-linux-mips64le": { 1608 | "version": "0.14.47", 1609 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz", 1610 | "integrity": "sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==", 1611 | "dev": true, 1612 | "optional": true 1613 | }, 1614 | "esbuild-linux-ppc64le": { 1615 | "version": "0.14.47", 1616 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz", 1617 | "integrity": "sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==", 1618 | "dev": true, 1619 | "optional": true 1620 | }, 1621 | "esbuild-linux-riscv64": { 1622 | "version": "0.14.47", 1623 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz", 1624 | "integrity": "sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==", 1625 | "dev": true, 1626 | "optional": true 1627 | }, 1628 | "esbuild-linux-s390x": { 1629 | "version": "0.14.47", 1630 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz", 1631 | "integrity": "sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==", 1632 | "dev": true, 1633 | "optional": true 1634 | }, 1635 | "esbuild-netbsd-64": { 1636 | "version": "0.14.47", 1637 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz", 1638 | "integrity": "sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==", 1639 | "dev": true, 1640 | "optional": true 1641 | }, 1642 | "esbuild-openbsd-64": { 1643 | "version": "0.14.47", 1644 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz", 1645 | "integrity": "sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==", 1646 | "dev": true, 1647 | "optional": true 1648 | }, 1649 | "esbuild-sunos-64": { 1650 | "version": "0.14.47", 1651 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz", 1652 | "integrity": "sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==", 1653 | "dev": true, 1654 | "optional": true 1655 | }, 1656 | "esbuild-windows-32": { 1657 | "version": "0.14.47", 1658 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz", 1659 | "integrity": "sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==", 1660 | "dev": true, 1661 | "optional": true 1662 | }, 1663 | "esbuild-windows-64": { 1664 | "version": "0.14.47", 1665 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz", 1666 | "integrity": "sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==", 1667 | "dev": true, 1668 | "optional": true 1669 | }, 1670 | "esbuild-windows-arm64": { 1671 | "version": "0.14.47", 1672 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz", 1673 | "integrity": "sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==", 1674 | "dev": true, 1675 | "optional": true 1676 | }, 1677 | "escape-string-regexp": { 1678 | "version": "4.0.0", 1679 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1680 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1681 | "dev": true 1682 | }, 1683 | "estree-walker": { 1684 | "version": "0.6.1", 1685 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 1686 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 1687 | "dev": true 1688 | }, 1689 | "fill-range": { 1690 | "version": "7.0.1", 1691 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1692 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1693 | "dev": true, 1694 | "requires": { 1695 | "to-regex-range": "^5.0.1" 1696 | } 1697 | }, 1698 | "fsevents": { 1699 | "version": "2.3.2", 1700 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1701 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1702 | "dev": true, 1703 | "optional": true 1704 | }, 1705 | "glob-parent": { 1706 | "version": "5.1.2", 1707 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1708 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1709 | "dev": true, 1710 | "requires": { 1711 | "is-glob": "^4.0.1" 1712 | } 1713 | }, 1714 | "html-rewriter-wasm": { 1715 | "version": "0.4.1", 1716 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 1717 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 1718 | "dev": true 1719 | }, 1720 | "http-cache-semantics": { 1721 | "version": "4.1.0", 1722 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 1723 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", 1724 | "dev": true 1725 | }, 1726 | "ignore": { 1727 | "version": "5.2.0", 1728 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 1729 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 1730 | "dev": true 1731 | }, 1732 | "is-binary-path": { 1733 | "version": "2.1.0", 1734 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1735 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1736 | "dev": true, 1737 | "requires": { 1738 | "binary-extensions": "^2.0.0" 1739 | } 1740 | }, 1741 | "is-extglob": { 1742 | "version": "2.1.1", 1743 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1744 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1745 | "dev": true 1746 | }, 1747 | "is-glob": { 1748 | "version": "4.0.3", 1749 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1750 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1751 | "dev": true, 1752 | "requires": { 1753 | "is-extglob": "^2.1.1" 1754 | } 1755 | }, 1756 | "is-number": { 1757 | "version": "7.0.0", 1758 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1759 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1760 | "dev": true 1761 | }, 1762 | "kleur": { 1763 | "version": "4.1.5", 1764 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1765 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1766 | "dev": true 1767 | }, 1768 | "magic-string": { 1769 | "version": "0.25.9", 1770 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", 1771 | "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", 1772 | "dev": true, 1773 | "requires": { 1774 | "sourcemap-codec": "^1.4.8" 1775 | } 1776 | }, 1777 | "mime": { 1778 | "version": "3.0.0", 1779 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1780 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1781 | "dev": true 1782 | }, 1783 | "miniflare": { 1784 | "version": "2.6.0", 1785 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.6.0.tgz", 1786 | "integrity": "sha512-KDAQZV2aDZ044X1ihlCIa6DPdq1w3fUJFW4xZ+r+DPUxj9t1AuehjR9Fc6zCmZQrk12gLXDSZSyNft1ozm1X7Q==", 1787 | "dev": true, 1788 | "requires": { 1789 | "@miniflare/cache": "2.6.0", 1790 | "@miniflare/cli-parser": "2.6.0", 1791 | "@miniflare/core": "2.6.0", 1792 | "@miniflare/durable-objects": "2.6.0", 1793 | "@miniflare/html-rewriter": "2.6.0", 1794 | "@miniflare/http-server": "2.6.0", 1795 | "@miniflare/kv": "2.6.0", 1796 | "@miniflare/r2": "2.6.0", 1797 | "@miniflare/runner-vm": "2.6.0", 1798 | "@miniflare/scheduler": "2.6.0", 1799 | "@miniflare/shared": "2.6.0", 1800 | "@miniflare/sites": "2.6.0", 1801 | "@miniflare/storage-file": "2.6.0", 1802 | "@miniflare/storage-memory": "2.6.0", 1803 | "@miniflare/web-sockets": "2.6.0", 1804 | "kleur": "^4.1.4", 1805 | "semiver": "^1.1.0", 1806 | "source-map-support": "^0.5.20", 1807 | "undici": "5.5.1" 1808 | } 1809 | }, 1810 | "mustache": { 1811 | "version": "4.2.0", 1812 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1813 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1814 | "dev": true 1815 | }, 1816 | "nanoid": { 1817 | "version": "3.3.4", 1818 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 1819 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 1820 | "dev": true 1821 | }, 1822 | "node-forge": { 1823 | "version": "1.3.1", 1824 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 1825 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 1826 | "dev": true 1827 | }, 1828 | "normalize-path": { 1829 | "version": "3.0.0", 1830 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1831 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1832 | "dev": true 1833 | }, 1834 | "path-to-regexp": { 1835 | "version": "6.2.1", 1836 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", 1837 | "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", 1838 | "dev": true 1839 | }, 1840 | "picomatch": { 1841 | "version": "2.3.1", 1842 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1843 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1844 | "dev": true 1845 | }, 1846 | "readdirp": { 1847 | "version": "3.6.0", 1848 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1849 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1850 | "dev": true, 1851 | "requires": { 1852 | "picomatch": "^2.2.1" 1853 | } 1854 | }, 1855 | "rollup-plugin-inject": { 1856 | "version": "3.0.2", 1857 | "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", 1858 | "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", 1859 | "dev": true, 1860 | "requires": { 1861 | "estree-walker": "^0.6.1", 1862 | "magic-string": "^0.25.3", 1863 | "rollup-pluginutils": "^2.8.1" 1864 | } 1865 | }, 1866 | "rollup-plugin-node-polyfills": { 1867 | "version": "0.2.1", 1868 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", 1869 | "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", 1870 | "dev": true, 1871 | "requires": { 1872 | "rollup-plugin-inject": "^3.0.0" 1873 | } 1874 | }, 1875 | "rollup-pluginutils": { 1876 | "version": "2.8.2", 1877 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 1878 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 1879 | "dev": true, 1880 | "requires": { 1881 | "estree-walker": "^0.6.1" 1882 | } 1883 | }, 1884 | "selfsigned": { 1885 | "version": "2.0.1", 1886 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", 1887 | "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", 1888 | "dev": true, 1889 | "requires": { 1890 | "node-forge": "^1" 1891 | } 1892 | }, 1893 | "semiver": { 1894 | "version": "1.1.0", 1895 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1896 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1897 | "dev": true 1898 | }, 1899 | "set-cookie-parser": { 1900 | "version": "2.5.1", 1901 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 1902 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==", 1903 | "dev": true 1904 | }, 1905 | "source-map": { 1906 | "version": "0.6.1", 1907 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1908 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1909 | "dev": true 1910 | }, 1911 | "source-map-support": { 1912 | "version": "0.5.21", 1913 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1914 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1915 | "dev": true, 1916 | "requires": { 1917 | "buffer-from": "^1.0.0", 1918 | "source-map": "^0.6.0" 1919 | } 1920 | }, 1921 | "sourcemap-codec": { 1922 | "version": "1.4.8", 1923 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1924 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1925 | "dev": true 1926 | }, 1927 | "stack-trace": { 1928 | "version": "0.0.10", 1929 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1930 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1931 | "dev": true 1932 | }, 1933 | "streamsearch": { 1934 | "version": "1.1.0", 1935 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1936 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1937 | "dev": true 1938 | }, 1939 | "to-regex-range": { 1940 | "version": "5.0.1", 1941 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1942 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1943 | "dev": true, 1944 | "requires": { 1945 | "is-number": "^7.0.0" 1946 | } 1947 | }, 1948 | "undici": { 1949 | "version": "5.5.1", 1950 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", 1951 | "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==", 1952 | "dev": true 1953 | }, 1954 | "urlpattern-polyfill": { 1955 | "version": "4.0.3", 1956 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1957 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1958 | "dev": true 1959 | }, 1960 | "wrangler": { 1961 | "version": "2.0.23", 1962 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.0.23.tgz", 1963 | "integrity": "sha512-qMyK/pmHIrubxuJuXnCwcidY4LlQImcTTyOGoqMJtNz8y+pDfsluExSBpwqGSl3JPUQF2FiofqaBkj/iB8rvYw==", 1964 | "dev": true, 1965 | "requires": { 1966 | "@cloudflare/kv-asset-handler": "^0.2.0", 1967 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1968 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1969 | "blake3-wasm": "^2.1.5", 1970 | "chokidar": "^3.5.3", 1971 | "esbuild": "0.14.47", 1972 | "fsevents": "~2.3.2", 1973 | "miniflare": "^2.6.0", 1974 | "nanoid": "^3.3.3", 1975 | "path-to-regexp": "^6.2.0", 1976 | "selfsigned": "^2.0.1", 1977 | "xxhash-wasm": "^1.0.1" 1978 | } 1979 | }, 1980 | "ws": { 1981 | "version": "8.8.1", 1982 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1983 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1984 | "dev": true, 1985 | "requires": {} 1986 | }, 1987 | "xxhash-wasm": { 1988 | "version": "1.0.1", 1989 | "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.1.tgz", 1990 | "integrity": "sha512-Lc9CTvDrH2vRoiaUzz25q7lRaviMhz90pkx6YxR9EPYtF99yOJnv2cB+CQ0hp/TLoqrUsk8z/W2EN31T568Azw==", 1991 | "dev": true 1992 | }, 1993 | "youch": { 1994 | "version": "2.2.2", 1995 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 1996 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 1997 | "dev": true, 1998 | "requires": { 1999 | "@types/stack-trace": "0.0.29", 2000 | "cookie": "^0.4.1", 2001 | "mustache": "^4.2.0", 2002 | "stack-trace": "0.0.10" 2003 | } 2004 | } 2005 | } 2006 | } 2007 | --------------------------------------------------------------------------------