From 41be46e9435ea425306a6e3c1f423b6acd7e048d Mon Sep 17 00:00:00 2001 From: Elle <265860153+Reb-Elle-Art@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:56:40 -0400 Subject: [PATCH 001/125] [integrations] Add Telegram capture bot --- integrations/telegram-capture/README.md | 496 ++++++++++++++++++++ integrations/telegram-capture/metadata.json | 20 + 2 files changed, 516 insertions(+) create mode 100644 integrations/telegram-capture/README.md create mode 100644 integrations/telegram-capture/metadata.json diff --git a/integrations/telegram-capture/README.md b/integrations/telegram-capture/README.md new file mode 100644 index 000000000..848801968 --- /dev/null +++ b/integrations/telegram-capture/README.md @@ -0,0 +1,496 @@ +# Telegram Capture + +> **Add Telegram as a quick-capture interface for your Open Brain.** Send a message to your bot (DM or a private group) and it's automatically embedded, classified, and stored, with a threaded confirmation back in the chat. + +--- + +## What It Does + +Runs a Supabase Edge Function as a Telegram bot webhook. Every text message sent to the configured chat becomes a `thoughts` row with an embedding (`openai/text-embedding-3-small`) and LLM-extracted metadata (people, topics, action items, dates, type). The bot replies in-thread with a confirmation so you know capture succeeded. Optional `UPDATE_ON_EDIT` support re-embeds edited messages in place. + +--- + +## Prerequisites + +- A working Open Brain setup (Supabase project with the `thoughts` table and pgvector) +- A Telegram account (the bot is free) +- An [OpenRouter](https://openrouter.ai) API key +- Supabase CLI installed and logged in +- Shell access with `curl` and `openssl` + +**Cost**: Telegram is free. OpenRouter embedding + classification is the same as slack-capture, roughly **$0.10–0.30/month** for 20 captures per day. + +--- + +## Credential Tracker + +Fill these in as you go, you'll need all of them in Step 4: + +| Credential | Where it comes from | Value | +|---|---|---| +| `TELEGRAM_BOT_TOKEN` | BotFather (Step 1) | | +| `TELEGRAM_CAPTURE_CHAT_ID` | `getUpdates` (Step 2) | | +| `TELEGRAM_WEBHOOK_SECRET` | Invent one in Step 4 | | +| `OPENROUTER_API_KEY` | [openrouter.ai/keys](https://openrouter.ai/keys) | | +| `SUPABASE_URL` | Auto-injected by Supabase | (skip) | +| `SUPABASE_SERVICE_ROLE_KEY` | Auto-injected by Supabase | (skip) | + +--- + +## Steps + +### Step 1 — Create a Telegram bot via BotFather + +1. Open Telegram and start a chat with [@BotFather](https://t.me/BotFather). +2. Send `/newbot`. +3. Pick a display name, then a username. The username must end in `bot` (e.g. `my_open_brain_bot`). +4. BotFather replies with a token shaped like `123456789:ABCdefGhIJKlmnoPQRstUVwxYZ`. +5. Copy the token into your tracker as `TELEGRAM_BOT_TOKEN`. + +> [!WARNING] +> The bot token is a credential. Don't paste it into chats, commits, or screenshots. + +✅ **Done when:** You have a bot token and can open your bot's profile in Telegram. + +--- + +### Step 2 — Find your capture `chat_id` + +You need the numeric ID of the chat where captures will live. Pick one option below. + +> [!WARNING] +> **`chat_id` is not your bot's ID.** If you looked up your bot with @IDBot or @userinfobot *from inside the bot's chat*, that returns the bot's user ID, which is the wrong number. In a DM with your bot, `chat_id` equals **your own** user ID (the human on the other side). For groups, it's the group's ID, a negative number. + +**Option A — DM yourself (fastest, recommended)** + +1. From your personal Telegram account (not via your bot), start a chat with [@userinfobot](https://t.me/userinfobot) and send any message. +2. @userinfobot replies with your user ID, a positive integer like `987654321`. +3. That number is your `TELEGRAM_CAPTURE_CHAT_ID`. Messages you DM to your bot will come in under this chat ID. + +**Option B — Use `getUpdates` (works for DMs or groups)** + +1. Open the chat where you want captures to happen (a DM with your bot, or a group the bot's in). +2. Send any message in that chat. +3. In a browser, visit (replace `YOUR_BOT_TOKEN` with your real token, no angle brackets): + ``` + https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates + ``` +4. Find the first `"chat": { "id": ... }` block in the JSON. That number is your `TELEGRAM_CAPTURE_CHAT_ID`. + - DM: positive integer, e.g. `987654321` + - Group/supergroup: negative integer, e.g. `-1001234567890` + +**Option C — Private group setup (if using a group)** + +1. Create a Telegram group and add your bot as a member. +2. Disable Privacy Mode so the bot sees all messages, not only ones that @-mention it: message BotFather → `/mybots` → pick your bot → `Bot Settings` → `Group Privacy` → `Turn off`. Remove and re-add the bot afterward for the change to take effect. +3. Use Option B to read the group's `chat_id` from `getUpdates`. + +> [!NOTE] +> Supergroup IDs start with `-100`. Keep the minus sign and the `100` — they're part of the value, not a formatting artifact. + +✅ **Done when:** You have a numeric chat ID (positive for DM, negative for group) that is *not* the same as your bot's user ID. + +--- + +### Step 3 — Drop the function into your Supabase project + +From the root of your Supabase project: + +```bash +mkdir -p supabase/functions/telegram-capture +``` + +Create `supabase/functions/telegram-capture/index.ts` with the contents below: + +```typescript +import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; + +const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; +const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; +const OPENROUTER_API_KEY = Deno.env.get("OPENROUTER_API_KEY")!; +const TELEGRAM_BOT_TOKEN = Deno.env.get("TELEGRAM_BOT_TOKEN")!; +const TELEGRAM_CAPTURE_CHAT_ID = Deno.env.get("TELEGRAM_CAPTURE_CHAT_ID")!; +const TELEGRAM_WEBHOOK_SECRET = Deno.env.get("TELEGRAM_WEBHOOK_SECRET"); +const UPDATE_ON_EDIT = Deno.env.get("UPDATE_ON_EDIT") === "true"; + +const OPENROUTER_BASE = "https://openrouter.ai/api/v1"; +const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY); + +async function getEmbedding(text: string): Promise { + const r = await fetch(`${OPENROUTER_BASE}/embeddings`, { + method: "POST", + headers: { + "Authorization": `Bearer ${OPENROUTER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ model: "openai/text-embedding-3-small", input: text }), + }); + const d = await r.json(); + return d.data[0].embedding; +} + +async function extractMetadata(text: string): Promise> { + const r = await fetch(`${OPENROUTER_BASE}/chat/completions`, { + method: "POST", + headers: { + "Authorization": `Bearer ${OPENROUTER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: "openai/gpt-4o-mini", + response_format: { type: "json_object" }, + messages: [ + { + role: "system", + content: `Extract metadata from the user's captured thought. Return JSON with: +- "people": array of people mentioned (empty if none) +- "action_items": array of implied to-dos (empty if none) +- "dates_mentioned": array of dates YYYY-MM-DD (empty if none) +- "topics": array of 1-3 short topic tags (always at least one) +- "type": one of "observation", "task", "idea", "reference", "person_note" +Only extract what's explicitly there.`, + }, + { role: "user", content: text }, + ], + }), + }); + const d = await r.json(); + try { return JSON.parse(d.choices[0].message.content); } + catch { return { topics: ["uncategorized"], type: "observation" }; } +} + +async function replyInTelegram(chatId: number, replyToMessageId: number, text: string): Promise { + await fetch(`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chat_id: chatId, + reply_to_message_id: replyToMessageId, + text, + parse_mode: "Markdown", + }), + }); +} + +function buildConfirmation(metadata: Record, prefix: string): string { + let line = `${prefix} *${metadata.type || "thought"}*`; + if (Array.isArray(metadata.topics) && metadata.topics.length > 0) + line += ` - ${metadata.topics.join(", ")}`; + if (Array.isArray(metadata.people) && metadata.people.length > 0) + line += `\nPeople: ${metadata.people.join(", ")}`; + if (Array.isArray(metadata.action_items) && metadata.action_items.length > 0) + line += `\nAction items: ${metadata.action_items.join("; ")}`; + return line; +} + +Deno.serve(async (req: Request): Promise => { + try { + // Verify the secret token Telegram echoes in this header. Prevents random + // internet traffic from hitting the endpoint if anyone discovers the URL. + if (TELEGRAM_WEBHOOK_SECRET) { + const secret = req.headers.get("X-Telegram-Bot-Api-Secret-Token"); + if (secret !== TELEGRAM_WEBHOOK_SECRET) { + return new Response("unauthorized", { status: 401 }); + } + } + + const body = await req.json(); + const message = body.message ?? body.edited_message; + const isEdit = !!body.edited_message; + + // Ignore edits/bots/non-text, and anything outside the configured chat. + if (!message + || message.from?.is_bot + || !message.text + || String(message.chat.id) !== TELEGRAM_CAPTURE_CHAT_ID) { + return new Response("ok", { status: 200 }); + } + + const messageText: string = message.text; + const chatId: number = message.chat.id; + const messageId: number = message.message_id; + + if (messageText.trim() === "") return new Response("ok", { status: 200 }); + + // Dedupe by (chat_id, message_id). Telegram retries aggressively on slow responses. + const { data: existing } = await supabase + .from("thoughts") + .select("id") + .contains("metadata", { telegram_chat_id: chatId, telegram_message_id: messageId }) + .limit(1); + + if (existing && existing.length > 0) { + if (isEdit && UPDATE_ON_EDIT) { + const [embedding, metadata] = await Promise.all([ + getEmbedding(messageText), + extractMetadata(messageText), + ]); + const { error } = await supabase + .from("thoughts") + .update({ + content: messageText, + embedding, + metadata: { + ...metadata, + source: "telegram", + telegram_chat_id: chatId, + telegram_message_id: messageId, + edited: true, + }, + }) + .eq("id", existing[0].id); + + if (error) { + console.error("Supabase update error:", error); + await replyInTelegram(chatId, messageId, `Failed to update: ${error.message}`); + return new Response("error", { status: 500 }); + } + await replyInTelegram(chatId, messageId, buildConfirmation(metadata, "Updated as")); + } + // Retry duplicate, or edit with UPDATE_ON_EDIT off. Ack and move on. + return new Response("ok", { status: 200 }); + } + + // Edit for a message we never captured (e.g. edited before first delivery). Skip. + if (isEdit) return new Response("ok", { status: 200 }); + + const [embedding, metadata] = await Promise.all([ + getEmbedding(messageText), + extractMetadata(messageText), + ]); + + const { error } = await supabase.from("thoughts").insert({ + content: messageText, + embedding, + metadata: { + ...metadata, + source: "telegram", + telegram_chat_id: chatId, + telegram_message_id: messageId, + }, + }); + + if (error) { + console.error("Supabase insert error:", error); + await replyInTelegram(chatId, messageId, `Failed to capture: ${error.message}`); + return new Response("error", { status: 500 }); + } + + await replyInTelegram(chatId, messageId, buildConfirmation(metadata, "Captured as")); + return new Response("ok", { status: 200 }); + } catch (err) { + console.error("Function error:", err); + return new Response("error", { status: 500 }); + } +}); +``` + +✅ **Done when:** The file exists at `supabase/functions/telegram-capture/index.ts` and saves without TypeScript errors in your editor. + +--- + +### Step 4 — Set environment variables + +**4a. Generate your webhook secret and write it down** + +Run this to produce a random 64-character hex string: + +```bash +openssl rand -hex 32 +``` + +Copy the output into your credential tracker under `TELEGRAM_WEBHOOK_SECRET`. You will paste this exact value twice: once into Supabase here in Step 4b, and once into the `setWebhook` call in Step 6. They must match byte-for-byte. + +> [!WARNING] +> **Do not use `$(openssl rand -hex 32)` inline inside the `supabase secrets set` command.** The shell will generate a value, pass it to Supabase, and throw it away — you'll never know what it was, and you'll have no way to tell Telegram what value to match. Always generate first, save the output, then paste it as a literal string. + +**4b. Push all four secrets to Supabase** + +Replace each placeholder with your real value (no angle brackets): + +```bash +supabase secrets set \ + TELEGRAM_BOT_TOKEN="123456789:ABCdefGhIJKlmnoPQRstUVwxYZ" \ + TELEGRAM_CAPTURE_CHAT_ID="987654321" \ + TELEGRAM_WEBHOOK_SECRET="paste_the_hex_string_from_4a_here" \ + OPENROUTER_API_KEY="sk-or-v1-your-openrouter-key" +``` + +`SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are injected automatically by the Supabase runtime, so you don't set those yourself. + +> [!TIP] +> `TELEGRAM_WEBHOOK_SECRET` isn't something Telegram gives you — it's a value *you* invent and share between Supabase and Telegram. Telegram echoes it back in the `X-Telegram-Bot-Api-Secret-Token` header on every call, and the function rejects anything that doesn't match. That's how the function knows the request came from Telegram. + +**4c. (Optional) Enable edit handling** + +```bash +supabase secrets set UPDATE_ON_EDIT=true +``` + +When set, editing a captured Telegram message re-runs embedding and metadata extraction and updates the row in place rather than creating a duplicate. + +✅ **Done when:** `supabase secrets list` shows all four required keys *and* you still have the hex value from 4a saved somewhere you can copy from. + +--- + +### Step 5 — Deploy the edge function + +```bash +supabase functions deploy telegram-capture --no-verify-jwt +``` + +> [!IMPORTANT] +> `--no-verify-jwt` is required. Telegram won't send a Supabase JWT with its webhook calls. Authentication is handled inside the function by the secret-token check from Step 4, so you're not actually dropping auth, just moving it. + +Your function URL will look like: +``` +https://YOUR_PROJECT_REF.supabase.co/functions/v1/telegram-capture +``` + +(where `YOUR_PROJECT_REF` is the subdomain of your actual Supabase project). Keep the full URL handy for Step 6. + +✅ **Done when:** `supabase functions deploy` prints a success URL. + +--- + +### Step 6 — Register the webhook with Telegram + +**6a. Sanity-check your bot token** + +Before the full registration, confirm the token itself works: + +```bash +curl "https://api.telegram.org/botYOUR_BOT_TOKEN/getMe" +``` + +Replace `YOUR_BOT_TOKEN` with your real token (no angle brackets, no spaces). A working response looks like: + +```json +{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"YourBot","username":"your_bot"}} +``` + +If you get `{"ok":false,"error_code":404,"description":"Not Found"}`, the token is wrong, truncated, or still has placeholder characters in it. Fix that before moving on. + +**6b. Call `setWebhook`** + +Run this **on one line** (don't reformat with backslashes — those can break depending on your shell): + +```bash +curl -X POST "https://api.telegram.org/botYOUR_BOT_TOKEN/setWebhook" -H "Content-Type: application/json" -d '{"url":"https://YOUR_PROJECT_REF.supabase.co/functions/v1/telegram-capture","secret_token":"YOUR_WEBHOOK_SECRET","allowed_updates":["message","edited_message"]}' +``` + +> [!IMPORTANT] +> **Three things to replace, everything else stays:** +> - `YOUR_BOT_TOKEN` → your real bot token from BotFather (the whole `123456789:ABC...` string) +> - `YOUR_PROJECT_REF` → the subdomain of your Supabase function URL. If your function lives at `https://abcdefg.supabase.co/functions/v1/telegram-capture`, then `abcdefg` is your project ref. +> - `YOUR_WEBHOOK_SECRET` → the hex value you generated in Step 4a and stored as `TELEGRAM_WEBHOOK_SECRET`. Must match exactly. +> +> The field names (`url`, `secret_token`, `allowed_updates`) are what Telegram's API looks for — leave those as written. Only change the *values* between the quotes. + +Expected response: + +```json +{"ok":true,"result":true,"description":"Webhook was set"} +``` + +**6c. Verify the webhook registered cleanly** + +```bash +curl "https://api.telegram.org/botYOUR_BOT_TOKEN/getWebhookInfo" +``` + +Check the JSON response: + +- `url` should show your full Supabase function URL +- `pending_update_count` should be `0` +- `last_error_message` field should be absent (if it's present, the webhook has been failing — read the message) + +**Common errors and what they mean:** + +| Response | Likely cause | +|---|---| +| `{"ok":false,"error_code":404,"description":"Not Found"}` | Bot token in the URL is wrong, truncated, or still has ``-style placeholder. | +| `curl: (3) URL rejected: Malformed input to a URL function` | URL contains angle brackets (`<...>`), a newline from a multi-line paste, or smart quotes (`"` vs `"`). | +| `{"ok":false,"error_code":400,"description":"Bad Request: bad webhook: ..."}` | `url` value in the JSON body is malformed or unreachable. Check your project ref. | +| `last_error_message: "Wrong response from the webhook: 401 Unauthorized"` | `secret_token` you sent here doesn't match `TELEGRAM_WEBHOOK_SECRET` on Supabase. | + +✅ **Done when:** `getWebhookInfo` returns your URL with `pending_update_count: 0` and no `last_error_message`. + +--- + +### Step 7 — Send a test message + +Open the chat (DM or group) and send: + +``` +Remind me to follow up with Sam about the pricing draft by Friday +``` + +Within a couple seconds the bot should reply in-thread with something like: + +``` +Captured as *task* - followups, pricing +People: Sam +Action items: follow up with Sam about pricing draft +``` + +Then confirm in Supabase: + +```sql +select id, content, metadata->>'type' as type, metadata->'topics' as topics +from thoughts +where metadata->>'source' = 'telegram' +order by created_at desc +limit 5; +``` + +✅ **Done when:** A new row appears with your message text, a populated embedding, and `metadata.source = 'telegram'`. + +--- + +## Expected Outcome + +Text messages sent to your configured chat are embedded, classified, and stored as `thoughts` rows, typically within two seconds. Each captured message gets a threaded confirmation listing its inferred type, topics, people, and action items. Duplicate webhook deliveries are silently deduped by `(telegram_chat_id, telegram_message_id)`. When `UPDATE_ON_EDIT=true`, editing a Telegram message updates the existing row in place. Non-text updates (stickers, photos without captions, join events, bot messages) are silently ignored. + +--- + +## Troubleshooting + +**Webhook returns 401 "unauthorized"** +The `secret_token` you passed to `setWebhook` doesn't match the `TELEGRAM_WEBHOOK_SECRET` environment variable. Re-run `setWebhook` with the correct value, or reset the secret via `supabase secrets set` and redeploy. + +**No capture, no error, messages are just ignored** +Run `curl "https://api.telegram.org/botYOUR_BOT_TOKEN/getWebhookInfo"` (replace with your actual token, no angle brackets). If `pending_update_count` is climbing or `last_error_message` is populated, the function is erroring. Check logs with `supabase functions logs telegram-capture`. If the function is invoked but returns `200` without inserting, confirm `String(message.chat.id) === TELEGRAM_CAPTURE_CHAT_ID`. A common mistake is storing `1234567890` when the real group ID is `-1001234567890` (the minus sign and `100` prefix matter). + +**Bot can't see messages in a group** +Non-admin bots only see messages that @-mention them. Either promote the bot to admin, or disable Privacy Mode via BotFather → `/mybots` → your bot → `Bot Settings` → `Group Privacy` → `Turn off`. After toggling, remove and re-add the bot for the change to take effect. + +**Duplicate rows on every message** +Look at `metadata->>'telegram_message_id'` on the duplicates. If they're the same, the dedup query isn't matching, verify that `thoughts.metadata` is JSONB and that the `contains` filter works against it. If the IDs are different, something upstream is replaying messages, which is not Telegram's usual behavior. + +**OpenRouter returns 401 or 429** +Grep `supabase functions logs telegram-capture` for those status codes. Regenerate the key or wait out the window. The function returns `500` to Telegram on OpenRouter failures, and Telegram will retry for up to 24 hours. + +--- + +## Tool Surface Area + +This integration **does not register any new MCP tools**. It is a capture-only ingestion path: an inbound webhook that writes to the existing `thoughts` table via a Supabase Edge Function. + +| Component | Type | What it does | +|---|---|---| +| `telegram-capture` Edge Function | Supabase webhook (not an MCP server) | Receives `message` and `edited_message` updates from Telegram, embeds the text via OpenRouter, extracts metadata, and inserts/updates a row in `thoughts`. | +| `thoughts` table | Existing Open Brain primitive | No schema changes. Rows written here are consumed by whatever MCP tools (search, retrieval, summarization) you've already installed. | + +**External services called:** `api.telegram.org` (webhook callback + send confirmation) and `openrouter.ai/api/v1` (embedding + classification). Both are outbound HTTPS; neither requires opening inbound ports beyond the Supabase function URL itself. + +**Auditing:** Because this integration adds no MCP tools, there's no MCP tool surface to audit for it directly. If you're installing this alongside MCP servers that read from the `thoughts` table (such as thought-search tools), audit those servers per the [MCP Tool Audit & Optimization Guide](../../docs/05-tool-audit.md). + +--- + +## Related + +- [Slack Capture](../slack-capture/) — same pattern for Slack +- [Discord Capture](../discord-capture/) — same pattern for Discord +- [MCP Tool Audit & Optimization Guide](../../docs/05-tool-audit.md) — recommended reading for any integration contributor +- [Contributing guide](../../CONTRIBUTING.md) — required reading before submitting changes diff --git a/integrations/telegram-capture/metadata.json b/integrations/telegram-capture/metadata.json new file mode 100644 index 000000000..a14d63985 --- /dev/null +++ b/integrations/telegram-capture/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Telegram Capture", + "description": "Add Telegram as a quick-capture interface for your Open Brain. Send a message to your bot (DM or private group) and it's embedded, classified, and stored automatically, with a threaded confirmation back in the chat.", + "category": "integrations", + "author": { + "name": "Elle", + "github": "Reb-Elle-Art" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Telegram Bot API", "OpenRouter"], + "tools": ["Supabase CLI"] + }, + "tags": ["telegram", "capture", "bot", "messaging"], + "difficulty": "beginner", + "estimated_time": "20 minutes", + "created": "2026-04-16", + "updated": "2026-04-16" +} From ba6b279ee09de56446138aa31e0dc1bbefef7a8f Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Fri, 17 Apr 2026 23:32:13 -0400 Subject: [PATCH 002/125] =?UTF-8?q?[recipes]=20Weekly=20digest=20=E2=80=94?= =?UTF-8?q?=20importance-ranked=20consumption=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scheduled script that queries the past N days of thoughts, paginates and ranks by importance, synthesizes with an LLM, and delivers to Telegram or stdout. Filters restricted/personal by default; opt in with --include-personal. --- recipes/weekly-digest/README.md | 252 ++++++++++++ recipes/weekly-digest/metadata.json | 20 + recipes/weekly-digest/weekly-digest.mjs | 502 ++++++++++++++++++++++++ 3 files changed, 774 insertions(+) create mode 100644 recipes/weekly-digest/README.md create mode 100644 recipes/weekly-digest/metadata.json create mode 100644 recipes/weekly-digest/weekly-digest.mjs diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md new file mode 100644 index 000000000..cffab8088 --- /dev/null +++ b/recipes/weekly-digest/README.md @@ -0,0 +1,252 @@ +# Weekly Digest + +> Scheduled importance-ranked synthesis of the past week's thoughts, delivered to Telegram (or stdout, or a file). + +## What It Does + +A weekly ritual that reads your brain back to you. Once a week, this script queries the last N days of `public.thoughts`, ranks them by importance, synthesizes them with an LLM (Claude Opus 4.7 by default), and delivers a short, sectioned digest — **Wins, Key decisions, Open loops, Themes** — to your Telegram chat. + +This is a "consumption format" companion to your capture habit. Captures alone pile up; a digest is the rhythm that turns the pile into something you actually revisit. Think of it as your weekly standup with yourself, written by the brain you fed it. + +## How It Works + +1. **Query** `public.thoughts` for the last `--window` days (default 7) via PostgREST. +2. **Filter** by `sensitivity_tier` — restricted is always excluded; personal is excluded by default (opt in with `--include-personal`). +3. **Paginate** the full window so a busy capture day can't push earlier high-signal thoughts out of the sample. Capped at 400 thoughts per run. +4. **Rank** by `importance` (highest first, recency as tiebreaker). If fewer than 10 thoughts clear the `--min-importance` threshold, the pool widens to the top 60 by importance + recency so a quiet week still produces something worth reading. +5. **Synthesize** via Claude (Anthropic API direct, or OpenRouter as fallback). The system prompt asks for a Telegram-formatted digest under 1500 characters with fixed sections. +6. **Deliver** to Telegram (default), stdout, or a local markdown file under `./digests/YYYY-MM-DD.md`. + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) +- Node.js 18+ (uses the native `fetch` API) +- An LLM credential — **one** of: + - `ANTHROPIC_API_KEY` (preferred — direct Anthropic API) + - `OPENROUTER_API_KEY` (fallback — routes through OpenRouter) +- **Optional:** a Telegram bot for delivery. If you don't have one, use `--output=stdout` or `--output=file` and skip the Telegram setup entirely. +- **Optional but recommended:** the [sensitivity-tiers primitive](../../primitives/) so `restricted` and `personal` filtering actually means something. If `sensitivity_tier` isn't present on your install, the script warns and falls back to an unfiltered query. + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +WEEKLY DIGEST -- CREDENTIAL TRACKER +-------------------------------------- + +FROM YOUR OPEN BRAIN SETUP + Open Brain URL: ____________ (your-project-ref.supabase.co) + Open Brain service key: ____________ + +LLM (pick one) + Anthropic API key: ____________ + OpenRouter API key: ____________ + +TELEGRAM DELIVERY (optional) + Bot token (from @BotFather): ____________ + Chat ID (your DM with bot): ____________ + +-------------------------------------- +``` + +## Installation + +1. Copy `weekly-digest.mjs` somewhere runnable (e.g., `~/scripts/weekly-digest.mjs` or inside an automation folder). +2. Export the required env vars for your shell, a `.env.local` file, or your scheduler's secret store. The script does not load `.env` files directly — keep it simple and let your runner (cron, systemd, GitHub Actions, `dotenv-cli`) handle that. + +```bash +export OPEN_BRAIN_URL="https://your-project-ref.supabase.co" +export OPEN_BRAIN_SERVICE_KEY="your-service-role-key" +export ANTHROPIC_API_KEY="sk-ant-..." # or OPENROUTER_API_KEY +export TELEGRAM_BOT_TOKEN="123456:..." # optional +export TELEGRAM_CHAT_ID="987654321" # optional +``` + +3. Smoke test it with `--dry-run` first so nothing ships anywhere: + +```bash +node weekly-digest.mjs --dry-run --output=stdout --window=7 +``` + +You should see a `───── DIGEST ─────` block printed to stdout. + +## Usage + +```bash +# Full defaults: 7-day window, Opus 4.7, delivered to Telegram +node weekly-digest.mjs + +# Print to console only +node weekly-digest.mjs --output=stdout + +# Write to ./digests/YYYY-MM-DD.md (creates ./digests/ if needed) +node weekly-digest.mjs --output=file + +# Two weeks of context +node weekly-digest.mjs --window=14 + +# Cheap run on Haiku +node weekly-digest.mjs --model=haiku + +# Lower importance threshold for a quieter week +node weekly-digest.mjs --min-importance=3 + +# Opt in to personal thoughts +node weekly-digest.mjs --include-personal +``` + +### Flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--window=` | `7` | How many days back to look | +| `--min-importance=` | `4` | Threshold for the primary pool (widens if too few thoughts clear it) | +| `--model=` | `claude-opus-4-7` | Model id, or one of the aliases `opus`, `sonnet`, `haiku` | +| `--output=` | `telegram` | `telegram`, `stdout`, or `file` | +| `--include-personal` | off | Also include `sensitivity_tier=personal` thoughts | +| `--dry-run` | off | Synthesize + print, deliver nothing | +| `-h`, `--help` | — | Show help and exit | + +### Getting a Telegram chat ID + +1. Create a bot with [@BotFather](https://t.me/BotFather) → save the HTTP token. +2. Send any message to your new bot. +3. Visit `https://api.telegram.org/bot/getUpdates` in a browser; find `"chat":{"id": 12345}` — that number is your `TELEGRAM_CHAT_ID`. + +If you already have a Telegram bot wired to Open Brain for capture (e.g., via a `telegram-capture` integration), you can reuse that same bot for delivery — this digest just pushes messages outbound, so it doesn't care whether the bot also accepts inbound captures. + +## Sensitivity + +Open Brain's [sensitivity-tiers](../../primitives/) primitive adds a `sensitivity_tier` column to `public.thoughts` with values like `standard`, `personal`, and `restricted`. The digest honors that signal: + +- **`restricted`** — never included. These are thoughts you've explicitly flagged as off-limits; a synthesis pass that surfaces them to a Telegram chat defeats the purpose of the tier. +- **`personal`** — excluded by default. Your week probably contains private material (health, relationships, finances) that you'd rather not summarize over an unencrypted wire to a third-party API. Pass `--include-personal` when you want the fuller picture — e.g., a private file output you keep locally. +- **`standard`** (or null) — always included. + +If the `sensitivity_tier` column isn't present on your install, the script logs a one-time warning and continues unfiltered. Install the sensitivity-tiers primitive to activate the filter. + +> [!IMPORTANT] +> Filtering happens at the PostgREST query level, not after the fact. Restricted thoughts never leave the database, so they can never reach the LLM. + +## Scheduling + +### Linux / macOS cron + +Run weekly on Sunday at 8am local time: + +```cron +0 8 * * 0 cd /path/to/recipe && /usr/bin/node weekly-digest.mjs >> ~/logs/weekly-digest.log 2>&1 +``` + +Make sure the crontab either inherits your env vars or sources them, e.g.: + +```cron +0 8 * * 0 . "$HOME/.weekly-digest.env" && /usr/bin/node /path/to/weekly-digest.mjs +``` + +### Windows Task Scheduler + +Create a new basic task: + +- **Trigger:** Weekly, Sunday, 8:00 AM +- **Action:** Start a program +- **Program:** `node.exe` (full path, e.g. `C:\Program Files\nodejs\node.exe`) +- **Arguments:** `C:\path\to\weekly-digest.mjs` +- **Start in:** `C:\path\to\` (the folder containing the script) +- **Environment variables:** set them in the "Actions → Edit → Environment" dialog, or wrap the command in a `.cmd` that exports them first. + +### GitHub Actions + +```yaml +name: Weekly Digest + +on: + schedule: + - cron: "0 13 * * 0" # Sundays 1pm UTC + workflow_dispatch: {} + +jobs: + digest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - name: Run digest + env: + OPEN_BRAIN_URL: ${{ secrets.OPEN_BRAIN_URL }} + OPEN_BRAIN_SERVICE_KEY: ${{ secrets.OPEN_BRAIN_SERVICE_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + run: node recipes/weekly-digest/weekly-digest.mjs +``` + +Store the five env vars as GitHub Actions secrets. The service key should be repo-level, not org-wide. + +## Cost Notes + +A single run sends up to 80 thoughts (each trimmed to 280 chars) plus metadata to the synthesizer. Rough token math: + +| Model | ~Input tokens / 80 thoughts | ~Output tokens | ~Cost / run | ~Cost / year (weekly) | +|-------|------------------------------|----------------|-------------|------------------------| +| `claude-opus-4-7` | ~8k | ~800 | ~$0.18 | **~$9** | +| `claude-sonnet-4-6` | ~8k | ~800 | ~$0.03 | ~$1.60 | +| `claude-haiku-4-5` | ~8k | ~800 | ~$0.008 | ~$0.40 | + +Opus is the default for a reason: in side-by-side runs it produces the kind of observation that makes a weekly ritual worth opening ("rescue impulse rooted in discomfort with being seen as unkind") versus Haiku's filler ("automation pays"). At roughly $9/year for Opus vs $0.40/year for Haiku, the Opus premium is small enough that most people will want it for the weekly cadence. Bulk or daily cadence changes that math — use `--model=haiku` there. + +Telegram posts are free. File output is free. PostgREST reads are free (your Supabase plan). + +## Expected Outcome + +Running `node weekly-digest.mjs --dry-run --output=stdout` against a brain with recent captures should print something like: + +``` +[weekly-digest] window=7d min_importance=4 model=claude-opus-4-7 output=stdout include_personal=false +[weekly-digest] fetched 142 thoughts from window +[weekly-digest] ranked pool: 37 thoughts +[weekly-digest] synthesized 1247 chars +───── DIGEST ───── +Weekly Digest — Apr 11 – Apr 17 + +Wins +- Shipped smart-ingest edge function with full review gate +- Telegram capture integration merged upstream +- ... + +Key decisions +- ... + +Open loops +- ... + +Themes +- ... +───── END ───── +[weekly-digest] --dry-run set; skipping delivery +``` + +With `--output=telegram`, the digest arrives as one (or two, if long) messages in your configured chat. With `--output=file`, a markdown file lands in `./digests/YYYY-MM-DD.md` with YAML frontmatter suitable for Obsidian or any file-based notes tool. + +## Troubleshooting + +**Issue: `Missing OPEN_BRAIN_URL env var`** +Solution: Export `OPEN_BRAIN_URL` and `OPEN_BRAIN_SERVICE_KEY` before running. The script does not auto-load `.env` files — wrap the call in `dotenv-cli` (`npx dotenv -e .env.local -- node weekly-digest.mjs`) or let your scheduler inject the env. + +**Issue: `sensitivity_tier column not found — falling back to unfiltered query`** +Solution: Your Open Brain install doesn't have the sensitivity-tiers primitive yet. The script still works, but restricted/personal thoughts will be included in the pool. Install the primitive to enable filtering, or audit the output carefully if you're posting publicly. + +**Issue: `thoughts fetch failed: 401`** +Solution: `OPEN_BRAIN_SERVICE_KEY` is wrong or expired. This must be the **service_role** key (not the anon key), because the recipe needs to read rows regardless of RLS policies. Double-check in Supabase → Project Settings → API. + +**Issue: `Telegram sendMessage failed: 400 chat not found`** +Solution: `TELEGRAM_CHAT_ID` is wrong, or you haven't messaged the bot yet. Send any message to your bot, then check `https://api.telegram.org/bot/getUpdates` — the numeric `chat.id` is what you want. + +**Issue: `no thoughts in window; nothing to digest`** +Solution: Either the window really is empty, or your service key can't see the rows. Run the same date filter manually in SQL — `SELECT COUNT(*) FROM thoughts WHERE created_at >= NOW() - INTERVAL '7 days';` — to confirm which it is. + +**Issue: Digest feels thin or generic** +Solution: Lower `--min-importance` (try `3`), widen `--window` (try `14`), or switch to `--model=opus` (the default — but confirm `DIGEST_MODEL` env var isn't overriding it). Haiku generates structurally similar output but with noticeably less insight per bullet. diff --git a/recipes/weekly-digest/metadata.json b/recipes/weekly-digest/metadata.json new file mode 100644 index 000000000..0ad15e12e --- /dev/null +++ b/recipes/weekly-digest/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Weekly Digest", + "description": "Scheduled importance-ranked synthesis of recent thoughts, delivered to Telegram", + "category": "recipes", + "author": { + "name": "Alan Shurafa", + "github": "alanshurafa" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Telegram Bot API (optional)", "Anthropic API or OpenRouter"], + "tools": ["Node.js 18+"] + }, + "tags": ["consumption-formats", "digest", "telegram", "synthesis", "scheduled"], + "difficulty": "intermediate", + "estimated_time": "30 minutes", + "created": "2026-04-17", + "updated": "2026-04-17" +} diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs new file mode 100644 index 000000000..34ff99004 --- /dev/null +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -0,0 +1,502 @@ +#!/usr/bin/env node +/** + * Weekly Digest for Open Brain + * ----------------------------- + * Synthesizes the past N days of thoughts into an importance-ranked digest + * and delivers it to Telegram, stdout, or a local markdown file. + * + * This is a "consumption format" — a rhythmic way to read your brain back + * to yourself. Captures on their own are input; the digest is the loop that + * turns a pile of thoughts into something you actually revisit. + * + * Usage: + * node weekly-digest.mjs # 7-day window → Telegram + * node weekly-digest.mjs --output=stdout # print to console only + * node weekly-digest.mjs --output=file # write to ./digests/YYYY-MM-DD.md + * node weekly-digest.mjs --window=14 # last 14 days + * node weekly-digest.mjs --include-personal # include sensitivity_tier=personal + * node weekly-digest.mjs --model=claude-haiku-4-5-20251001 + * node weekly-digest.mjs --min-importance=3 # lower threshold + * node weekly-digest.mjs --dry-run # synthesize + print, deliver nothing + * + * Env vars: + * OPEN_BRAIN_URL Your Supabase project URL (required) + * OPEN_BRAIN_SERVICE_KEY Supabase service role key (required) + * ANTHROPIC_API_KEY Direct Anthropic key (preferred) + * OPENROUTER_API_KEY OpenRouter fallback (used if ANTHROPIC_API_KEY unset) + * TELEGRAM_BOT_TOKEN Required for --output=telegram + * TELEGRAM_CHAT_ID Required for --output=telegram + * DIGEST_MODEL Override default model (default: claude-opus-4-7) + */ + +import fs from "node:fs"; +import path from "node:path"; + +// ── Config ────────────────────────────────────────────────────────────────── + +const DEFAULT_MODEL = process.env.DIGEST_MODEL || "claude-opus-4-7"; + +// Friendly aliases you can pass to --model. +const MODEL_ALIASES = { + opus: "claude-opus-4-7", + sonnet: "claude-sonnet-4-6", + haiku: "claude-haiku-4-5-20251001", +}; + +// Telegram messages over this many chars get split across multiple sends. +// Telegram's hard limit is 4096 for text messages. +const TELEGRAM_CHUNK_LIMIT = 3800; + +// How many thoughts we send to the synthesizer. The script paginates the +// full window above this cap, then ranks, then trims. Bigger = more context +// + more tokens + more cost. +const SYNTHESIZE_INPUT_CAP = 80; + +// Hard ceiling on total thoughts pulled from the brain per run, to protect +// against someone with a huge window + heavy capture volume blowing past +// what one LLM call can reasonably digest. +const FETCH_HARD_CAP = 400; + +// Page size for PostgREST pagination. +const PAGE_SIZE = 100; + +// ── Args ──────────────────────────────────────────────────────────────────── + +function parseArgs(argv) { + const args = { + window: 7, + minImportance: 4, + model: DEFAULT_MODEL, + output: "telegram", + includePersonal: false, + dryRun: false, + }; + for (const raw of argv) { + if (raw === "--dry-run") { + args.dryRun = true; + } else if (raw === "--include-personal") { + args.includePersonal = true; + } else if (raw.startsWith("--window=")) { + const n = Number(raw.slice("--window=".length)); + if (!Number.isFinite(n) || n <= 0) { + throw new Error(`--window must be a positive number, got: ${raw}`); + } + args.window = n; + } else if (raw.startsWith("--min-importance=")) { + const n = Number(raw.slice("--min-importance=".length)); + if (!Number.isFinite(n) || n < 0) { + throw new Error(`--min-importance must be >= 0, got: ${raw}`); + } + args.minImportance = n; + } else if (raw.startsWith("--model=")) { + const val = raw.slice("--model=".length).trim(); + args.model = MODEL_ALIASES[val] ?? val; + } else if (raw.startsWith("--output=")) { + const val = raw.slice("--output=".length).trim(); + if (!["telegram", "stdout", "file"].includes(val)) { + throw new Error(`--output must be telegram|stdout|file, got: ${val}`); + } + args.output = val; + } else if (raw === "--help" || raw === "-h") { + printHelp(); + process.exit(0); + } else if (raw.startsWith("--")) { + throw new Error(`Unknown flag: ${raw}`); + } + } + return args; +} + +function printHelp() { + console.log( + [ + "Weekly Digest — importance-ranked synthesis of recent thoughts", + "", + "Usage: node weekly-digest.mjs [options]", + "", + "Options:", + " --window= Lookback window in days (default: 7)", + " --min-importance= Minimum importance threshold (default: 4)", + " --model= LLM model (default: claude-opus-4-7)", + " Aliases: opus, sonnet, haiku", + " --output= telegram | stdout | file (default: telegram)", + " --include-personal Include sensitivity_tier=personal thoughts", + " --dry-run Synthesize + print, deliver nothing", + " -h, --help Show this help", + ].join("\n"), + ); +} + +// ── Env validation ────────────────────────────────────────────────────────── + +function loadConfig(args) { + const openBrainUrl = process.env.OPEN_BRAIN_URL; + const openBrainKey = process.env.OPEN_BRAIN_SERVICE_KEY; + if (!openBrainUrl) throw new Error("Missing OPEN_BRAIN_URL env var"); + if (!openBrainKey) throw new Error("Missing OPEN_BRAIN_SERVICE_KEY env var"); + + const anthropicKey = process.env.ANTHROPIC_API_KEY; + const openrouterKey = process.env.OPENROUTER_API_KEY; + if (!anthropicKey && !openrouterKey) { + throw new Error( + "Missing LLM credentials: set ANTHROPIC_API_KEY or OPENROUTER_API_KEY", + ); + } + + const llmProvider = anthropicKey ? "anthropic" : "openrouter"; + const llmKey = anthropicKey || openrouterKey; + + let telegramBotToken = null; + let telegramChatId = null; + if (args.output === "telegram") { + telegramBotToken = process.env.TELEGRAM_BOT_TOKEN; + telegramChatId = process.env.TELEGRAM_CHAT_ID; + if (!telegramBotToken || !telegramChatId) { + throw new Error( + "--output=telegram requires TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID env vars. " + + "Use --output=stdout or --output=file if you don't have Telegram configured.", + ); + } + } + + // Normalize the base URL: strip trailing slashes so we can concat cleanly. + const baseUrl = openBrainUrl.replace(/\/+$/, ""); + + return { + baseUrl, + serviceKey: openBrainKey, + llmProvider, + llmKey, + telegramBotToken, + telegramChatId, + }; +} + +// ── Thoughts fetch via PostgREST ──────────────────────────────────────────── + +/** + * Fetches thoughts from public.thoughts for the last `windowDays` days, + * excluding restricted (always) and personal (unless --include-personal). + * + * Why PostgREST direct vs. an edge function: the core Open Brain install + * ships public.thoughts as a PostgREST-reachable table. Going direct keeps + * this recipe runnable on a stock Open Brain without requiring a custom + * REST gateway edge function. + */ +async function fetchThoughts(cfg, { windowDays, includePersonal }) { + const sinceIso = new Date(Date.now() - windowDays * 86_400_000).toISOString(); + + // Build the sensitivity exclusion list. sensitivity_tier is a TEXT column + // added by the sensitivity-tiers primitive; if a given Open Brain install + // doesn't have it, PostgREST will 400 and we fall back to a query with no + // sensitivity filter (see fallback below). + const excluded = includePersonal ? ["restricted"] : ["restricted", "personal"]; + const excludeFilter = `&sensitivity_tier=not.in.(${excluded.join(",")})`; + + const headers = { + apikey: cfg.serviceKey, + Authorization: `Bearer ${cfg.serviceKey}`, + }; + + const all = []; + let useFilter = true; + + for (let offset = 0; offset < FETCH_HARD_CAP; offset += PAGE_SIZE) { + const limit = Math.min(PAGE_SIZE, FETCH_HARD_CAP - offset); + const url = + `${cfg.baseUrl}/rest/v1/thoughts` + + `?select=id,content,created_at,importance,metadata,sensitivity_tier` + + `&created_at=gte.${sinceIso}` + + `&order=created_at.desc` + + `&limit=${limit}&offset=${offset}` + + (useFilter ? excludeFilter : ""); + + const res = await fetch(url, { headers }); + + // If sensitivity_tier column doesn't exist on this install, PostgREST + // returns 400 with "column does not exist". Retry once without filter + // and warn — the user may not have the sensitivity-tiers primitive yet. + if (!res.ok && useFilter && res.status === 400) { + const text = await res.text(); + if (/sensitivity_tier/.test(text)) { + console.warn( + "[weekly-digest] sensitivity_tier column not found — " + + "falling back to unfiltered query. Install the sensitivity-tiers " + + "primitive to enable restricted/personal filtering.", + ); + useFilter = false; + offset -= PAGE_SIZE; // retry this page without the filter + continue; + } + throw new Error(`thoughts fetch failed: ${res.status} ${text}`); + } + + if (!res.ok) { + throw new Error(`thoughts fetch failed: ${res.status} ${await res.text()}`); + } + + const batch = await res.json(); + if (!Array.isArray(batch) || batch.length === 0) break; + all.push(...batch); + if (batch.length < limit) break; + } + + return all; +} + +/** + * Ranks the fetched pool by importance, falling back to recency when + * importance ties or is missing. If there aren't enough thoughts at or above + * the minImportance threshold we widen the pool so the digest doesn't come + * out thin — a week with few high-importance thoughts should still produce + * something worth reading. + */ +function rankAndTrim(thoughts, minImportance) { + const sorted = [...thoughts].sort((a, b) => { + const ai = a.importance ?? 0; + const bi = b.importance ?? 0; + if (bi !== ai) return bi - ai; + return String(b.created_at).localeCompare(String(a.created_at)); + }); + + const highImportance = sorted.filter((t) => (t.importance ?? 0) >= minImportance); + const pool = highImportance.length >= 10 ? highImportance : sorted.slice(0, 60); + return pool.slice(0, 200); +} + +// ── LLM synthesis ─────────────────────────────────────────────────────────── + +const SYSTEM_PROMPT = + "You write tight weekly digests for a personal second brain. " + + "Output plain text formatted for a Telegram chat (NOT markdown). " + + "Use section headers with emoji, short bullets. Max 1500 characters total. " + + "Sections: Wins, Key decisions, Open loops, Themes. " + + "Be specific — name projects and tasks. Skip filler."; + +function buildUserPrompt(thoughts, startDate, endDate) { + const fmt = (d) => + d.toLocaleDateString("en-US", { month: "short", day: "numeric" }); + + const rows = thoughts.slice(0, SYNTHESIZE_INPUT_CAP).map((t) => ({ + id: t.id, + date: String(t.created_at || "").slice(0, 10), + type: t.metadata?.type ?? null, + importance: t.importance ?? null, + content: String(t.content || "").slice(0, 280), + topics: (t.metadata?.topics ?? []).slice(0, 5), + tags: (t.metadata?.tags ?? []).slice(0, 5), + })); + + return ( + `Weekly digest for ${fmt(startDate)} – ${fmt(endDate)}.\n` + + `Source: ${rows.length} high-signal thoughts.\n\n` + + `INPUT:\n${JSON.stringify(rows)}\n\n` + + `Produce the digest now.` + ); +} + +async function synthesizeAnthropic(cfg, model, systemPrompt, userPrompt) { + const res = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + headers: { + "x-api-key": cfg.llmKey, + "anthropic-version": "2023-06-01", + "content-type": "application/json", + }, + body: JSON.stringify({ + model, + max_tokens: 1024, + system: systemPrompt, + messages: [{ role: "user", content: userPrompt }], + }), + }); + if (!res.ok) { + throw new Error(`Anthropic call failed: ${res.status} ${await res.text()}`); + } + const body = await res.json(); + return body?.content?.[0]?.text?.trim() || ""; +} + +async function synthesizeOpenRouter(cfg, model, systemPrompt, userPrompt) { + // OpenRouter uses the OpenAI chat/completions shape. For Claude models we + // prefix "anthropic/" unless the caller already passed a slash-namespaced + // model id (e.g. "anthropic/claude-opus-4-7" or "openai/gpt-4o"). + const namespacedModel = model.includes("/") ? model : `anthropic/${model}`; + + const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${cfg.llmKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: namespacedModel, + max_tokens: 1024, + messages: [ + { role: "system", content: systemPrompt }, + { role: "user", content: userPrompt }, + ], + }), + }); + if (!res.ok) { + throw new Error(`OpenRouter call failed: ${res.status} ${await res.text()}`); + } + const body = await res.json(); + return body?.choices?.[0]?.message?.content?.trim() || ""; +} + +async function synthesize(cfg, thoughts, windowDays, model) { + const endDate = new Date(); + const startDate = new Date(Date.now() - (windowDays - 1) * 86_400_000); + const userPrompt = buildUserPrompt(thoughts, startDate, endDate); + + const text = + cfg.llmProvider === "anthropic" + ? await synthesizeAnthropic(cfg, model, SYSTEM_PROMPT, userPrompt) + : await synthesizeOpenRouter(cfg, model, SYSTEM_PROMPT, userPrompt); + + if (!text) throw new Error("LLM returned empty digest"); + return { text, startDate, endDate }; +} + +// ── Delivery ──────────────────────────────────────────────────────────────── + +/** + * Telegram text messages cap at 4096 chars. If the digest goes over, split + * on paragraph boundaries so we don't chop mid-bullet. + */ +function chunkForTelegram(text, limit = TELEGRAM_CHUNK_LIMIT) { + if (text.length <= limit) return [text]; + const chunks = []; + let remaining = text; + while (remaining.length > limit) { + // Prefer to break on a double newline (paragraph), else single newline. + let cut = remaining.lastIndexOf("\n\n", limit); + if (cut < limit * 0.5) cut = remaining.lastIndexOf("\n", limit); + if (cut < limit * 0.5) cut = limit; + chunks.push(remaining.slice(0, cut).trimEnd()); + remaining = remaining.slice(cut).trimStart(); + } + if (remaining.length > 0) chunks.push(remaining); + return chunks; +} + +async function deliverTelegram(cfg, text) { + const url = `https://api.telegram.org/bot${cfg.telegramBotToken}/sendMessage`; + const messageIds = []; + for (const chunk of chunkForTelegram(text)) { + const res = await fetch(url, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + chat_id: cfg.telegramChatId, + text: chunk, + disable_web_page_preview: true, + }), + }); + if (!res.ok) { + throw new Error( + `Telegram sendMessage failed: ${res.status} ${await res.text()}`, + ); + } + const body = await res.json(); + if (body?.result?.message_id) messageIds.push(body.result.message_id); + } + return messageIds; +} + +function deliverFile(text, startDate, endDate, model, sourceCount) { + const dir = path.resolve(process.cwd(), "digests"); + fs.mkdirSync(dir, { recursive: true }); + const filename = `${endDate.toISOString().slice(0, 10)}.md`; + const filepath = path.join(dir, filename); + + const frontmatter = [ + "---", + `title: Weekly Digest ${endDate.toISOString().slice(0, 10)}`, + "type: weekly-digest", + `period_start: ${startDate.toISOString().slice(0, 10)}`, + `period_end: ${endDate.toISOString().slice(0, 10)}`, + `generated_at: ${new Date().toISOString()}`, + `generated_by_model: ${model}`, + `source_thought_count: ${sourceCount}`, + "tags: [weekly-digest, synthesis]", + "---", + "", + ].join("\n"); + + fs.writeFileSync(filepath, frontmatter + text + "\n", "utf8"); + return filepath; +} + +// ── Main ──────────────────────────────────────────────────────────────────── + +async function main() { + const args = parseArgs(process.argv.slice(2)); + const cfg = loadConfig(args); + + console.log( + `[weekly-digest] window=${args.window}d min_importance=${args.minImportance} ` + + `model=${args.model} output=${args.output} include_personal=${args.includePersonal}`, + ); + + const pool = await fetchThoughts(cfg, { + windowDays: args.window, + includePersonal: args.includePersonal, + }); + console.log(`[weekly-digest] fetched ${pool.length} thoughts from window`); + + if (pool.length === 0) { + console.log("[weekly-digest] no thoughts in window; nothing to digest"); + return; + } + + const ranked = rankAndTrim(pool, args.minImportance); + console.log(`[weekly-digest] ranked pool: ${ranked.length} thoughts`); + + const { text: digest, startDate, endDate } = await synthesize( + cfg, + ranked, + args.window, + args.model, + ); + console.log(`[weekly-digest] synthesized ${digest.length} chars`); + console.log("───── DIGEST ─────"); + console.log(digest); + console.log("───── END ─────"); + + if (args.dryRun) { + console.log("[weekly-digest] --dry-run set; skipping delivery"); + return; + } + + if (args.output === "stdout") { + // Digest already printed above; nothing more to do. + return; + } + + if (args.output === "file") { + const filepath = deliverFile( + digest, + startDate, + endDate, + args.model, + ranked.length, + ); + console.log(`[weekly-digest] wrote ${filepath}`); + return; + } + + if (args.output === "telegram") { + const ids = await deliverTelegram(cfg, digest); + console.log( + `[weekly-digest] posted to Telegram (${ids.length} message${ids.length === 1 ? "" : "s"}): ${ids.join(", ")}`, + ); + return; + } +} + +main().catch((err) => { + console.error("[weekly-digest] FAILED:", err?.message || err); + process.exit(1); +}); From 15d8716511251ba16293d3945053a8e9f329227f Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Fri, 17 Apr 2026 23:35:43 -0400 Subject: [PATCH 003/125] =?UTF-8?q?[recipes]=20Lint=20sweep=20=E2=80=94=20?= =?UTF-8?q?bounded=20brain=20quality=20audit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weekly quality audit across three cost tiers: SQL-only orphan/dup lint (free), graph-based edge-weakness lint (free), and LLM-assisted contradiction sampling (budget-capped). Read-only — produces a report, never mutates. --- recipes/lint-sweep/README.md | 263 +++++++++++ recipes/lint-sweep/lint-sweep.mjs | 708 ++++++++++++++++++++++++++++++ recipes/lint-sweep/metadata.json | 20 + recipes/lint-sweep/views.sql | 138 ++++++ 4 files changed, 1129 insertions(+) create mode 100644 recipes/lint-sweep/README.md create mode 100644 recipes/lint-sweep/lint-sweep.mjs create mode 100644 recipes/lint-sweep/metadata.json create mode 100644 recipes/lint-sweep/views.sql diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md new file mode 100644 index 000000000..a411142e9 --- /dev/null +++ b/recipes/lint-sweep/README.md @@ -0,0 +1,263 @@ +# Lint Sweep + +> Bounded weekly audit that scans your brain for orphans, contradictions, stale facts, and low-signal noise — then writes a human-reviewable markdown report. Never mutates thoughts. + +## What It Does + +Lint Sweep is a read-only quality audit for your Open Brain. It runs the same way a code linter runs — scan the content, flag suspicious items, leave everything to a human to fix. Three cost tiers let you trade completeness for budget: Tier 1 runs entirely in SQL (free), Tier 2 walks the knowledge graph (free), and Tier 3 uses an LLM to sample a small batch of thoughts for contradictions and missing-links (capped LLM spend). + +Inspired by Andrej Karpathy's "lint the wiki" pattern and the [CRATE CLI](https://github.com/GuiminChen/CRATE) compile/ask/lint/ingest loop. + +## Tiers + +| Tier | What it checks | Cost | Runs for | +| ---- | -------------- | ---- | -------- | +| **1 — SQL-only** | Orphans by tag, exact fingerprint duplicates, missing fingerprints, low-signal noise, over-tagged thoughts, empty content, unchunked dumps | $0 (no LLM) | Any brain, any size | +| **2 — Graph-based** | High-importance thoughts with no entity links, entities with zero edges | $0 (no LLM) | Brains that have the [ob-graph](../ob-graph/) recipe applied | +| **3 — LLM-assisted** | Semantic contradictions, stale facts, superseded decisions, missing-link suggestions, orphan content, low-signal despite high importance | ~$0.01–0.05 per 100 thoughts sampled (Claude Haiku via OpenRouter) | Brains with OpenRouter key configured | + +Tier 1 and Tier 2 run against your Supabase project via PostgREST and complete in seconds against a 100K-thought brain. Tier 3 is the only tier that calls an external API — and it is hard-capped by `--max-llm-calls`. + +## Prerequisites + +- Working [Open Brain setup](../../docs/01-getting-started.md) with `public.thoughts` populated +- Node.js 18 or later +- (Optional, Tier 2) [ob-graph](../ob-graph/) recipe applied for graph-based lint +- (Optional, Tier 3) An OpenRouter API key with credit available + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +LINT SWEEP -- CREDENTIAL TRACKER +-------------------------------------- + +FROM YOUR OPEN BRAIN SETUP + Supabase project URL: ____________ (OPEN_BRAIN_URL) + Supabase service role key: ____________ (OPEN_BRAIN_SERVICE_KEY) + +OPTIONAL (TIER 3 ONLY) + OpenRouter API key: ____________ (OPENROUTER_API_KEY) + +-------------------------------------- +``` + +## Installation + +1. Copy the recipe into a working directory on the machine that will run the sweep (your laptop, a VPS, or a CI runner): + + ```bash + cp -r recipes/lint-sweep ~/lint-sweep + cd ~/lint-sweep + ``` + +2. Create a `.env.local` file in that directory with your credentials: + + ```bash + cat > .env.local <<'EOF' + OPEN_BRAIN_URL=https://your-project.supabase.co + OPEN_BRAIN_SERVICE_KEY=your-service-role-key + # Optional: OPENROUTER_API_KEY=sk-or-v1-... + EOF + chmod 600 .env.local + ``` + + > [!WARNING] + > The service role key bypasses Row Level Security. Keep `.env.local` out of version control and restrict its file permissions. + +3. (Optional) Apply the SQL views if you want to run Tier 1 checks directly in Supabase Studio without the Node script: + + Open Supabase → SQL Editor → paste the contents of [`views.sql`](./views.sql) → Run. This creates read-only views (`lint_orphans_by_tag`, `lint_exact_duplicates`, `lint_high_importance_isolated`, etc.) you can query any time. + +4. Verify the script runs: + + ```bash + node lint-sweep.mjs --tier=1 + ``` + + You should see progress output and a `lint-report-YYYY-MM-DD.md` file in your working directory. + +## Usage + +Run every tier against the defaults: + +```bash +node lint-sweep.mjs +``` + +Pick a single tier: + +```bash +node lint-sweep.mjs --tier=1 # SQL-only, free +node lint-sweep.mjs --tier=2 # graph-based, free +node lint-sweep.mjs --tier=3 --max-llm-calls=5 # LLM, capped +``` + +Custom output path and sample size: + +```bash +node lint-sweep.mjs \ + --tier=all \ + --sample-size=200 \ + --max-llm-calls=10 \ + --report=./reports/weekly-$(date +%F).md +``` + +All flags: + +| Flag | Default | Meaning | +| ---- | ------- | ------- | +| `--tier=<1\|2\|3\|all>` | `all` | Which tier(s) to run | +| `--sample-size=` | `100` | Tier 3 sample size (bounded by `--max-llm-calls × 20`) | +| `--max-llm-calls=` | `5` | Hard cap on Tier 3 LLM calls (each audits ~20 thoughts) | +| `--report=` | `./lint-report-YYYY-MM-DD.md` | Where to write the markdown report | +| `--days=` | `365` | Tier 3 recency window | +| `--llm-model=` | `anthropic/claude-haiku-4-5` | OpenRouter model for Tier 3 | +| `--verbose` / `-v` | off | Print progress per LLM call | +| `--help` / `-h` | — | Show usage | + +## Report Format + +The output is a self-contained markdown file ready for human review. Sample output from a small test brain: + +```markdown +--- +title: Lint Sweep — 2026-04-17 +generated_at: 2026-04-17T14:22:11.031Z +tier: all +started_at: 2026-04-17T14:22:04.112Z +finished_at: 2026-04-17T14:22:11.031Z +--- + +# Open Brain Lint Sweep — 2026-04-17 + +*Read-only audit. This script never mutates thoughts.* + +## Summary + +- Total thoughts inspected (Tier 1 sample): 12847 +- Orphans by tag (recent 2000): 43 +- Exact-duplicate fingerprint groups: 2 +- Rows missing content_fingerprint: 0 +- Low-signal noise candidates: 18 +- High-importance isolated (no entity links): 7 +- Entities with zero edges: 12 +- LLM contradiction findings: 4 (over 100 thoughts, 5 LLM calls) + +## Tier 1 — SQL-only lint (free) + +- Orphans by tag (recent 2000 thoughts): **43** — thoughts with no topics, tags, or people. +- Over-tagged (>10 tags): **3** — typically import noise. + - thought #48221 → 14 tags + - thought #48219 → 12 tags +- Low-signal noise (importance ≤2, content <40 chars): **18** +- Exact-duplicate fingerprint groups: **2** + - fingerprint a1b2c3d4e5f6… → 2 copies (ids: 11032, 11418) + +## Tier 2 — Graph-based lint (free) + +- High-importance (≥4) thoughts with no entity links: **7** + - #12033 (imp=5, 2026-01-14) — Moving biweekly 1:1 from Thursday to Tuesday starting next month… + +## Tier 3 — LLM-assisted contradiction sampling (budgeted) + +- Sample size: **100** thoughts +- LLM calls: **5** (cap: 5) +- Model: `anthropic/claude-haiku-4-5` + +### Contradictions (2) + +*Two thoughts state incompatible facts.* + +- **#4821, #9102** — Both thoughts describe team size. #4821 says "5 engineers as of Jan 2025" and #9102 says "3 engineers" with no date. + - *Action:* Annotate #9102 with a date or mark it superseded. + +### Stale Facts (1) +... + +--- + +**Safety:** `lint-sweep.mjs` is read-only. Every finding above is a suggestion +for a human to review. Before acting on any item, verify the thought with +`get_thought` or the web UI. Never delete or edit a thought based solely on +this report. +``` + +## Expected Outcome + +After a successful run you should see: + +- A markdown report at the path you passed to `--report` (or `./lint-report-YYYY-MM-DD.md` by default). +- Console output showing progress per tier and final counts — something like `[tier 1] done — 12847 total thoughts, 43 orphans-by-tag, 2 dup groups, 0 missing-fingerprint`. +- No changes to your Open Brain. The result of the sweep is the report file — nothing else is written back to the database. + +The report is designed to be triaged by hand: scan each section, follow up on anything that looks real, ignore anything that is a false positive. Over several weeks you should see Tier 1 and Tier 2 counts shrink as you clean up obvious hygiene issues, leaving Tier 3 findings as the main source of ongoing work. + +## Cost Notes + +Tier 1 and Tier 2 are free — they only touch your Supabase project via PostgREST. + +Tier 3 is the only billed component. Using the default `anthropic/claude-haiku-4-5` model on OpenRouter, each call with ~20 thoughts (~6k input tokens + ~2k output tokens) runs about **$0.002 to $0.005** at current public pricing. The default cap of 5 calls covers 100 thoughts per sweep for **~$0.02**. Scaling up: + +| `--sample-size` | `--max-llm-calls` | Approx. cost per run | +| --------------- | ----------------- | -------------------- | +| 100 | 5 | ~$0.02 | +| 200 | 10 | ~$0.04 | +| 500 | 25 | ~$0.10 | +| 1000 | 50 | ~$0.20 | + +Pick a smaller, faster model (`anthropic/claude-haiku-4-5`) for cheap sweeps or a stronger one (`anthropic/claude-sonnet-4-5`) for weekly deep audits. The script does not retry on failure — a bad response aborts the run and leaves you with a partial report rather than silently burning credits. + +Set `--max-llm-calls=0` to disable Tier 3 explicitly without needing to edit the tier flag, and omit the `OPENROUTER_API_KEY` entirely to make Tier 3 skip with a logged reason. + +## Safety + +- **Read-only by design.** The script only uses `GET` against PostgREST and `POST /chat/completions` against OpenRouter. No `PATCH`, `POST`, `DELETE`, or RPC write calls to your brain. +- **No destructive defaults.** Nothing deletes, merges, updates importance, or edits thoughts. Every finding is a *suggestion* a human must act on via `update_thought`, `delete_thought`, or the web UI. +- **Budget caps are hard caps.** `--max-llm-calls` is enforced before the first LLM call — you cannot exceed it by any combination of other flags. +- **No secrets in the report.** The report contains thought IDs and content previews (first 200 chars). Treat it like the brain content itself — store it in a private repo or an `output/` directory ignored by git. +- **Fail-loud.** Any HTTP error (bad credentials, OpenRouter down, malformed JSON) aborts the run with a clear message rather than writing a partial, misleading report. + +## Scheduling + +Run the sweep weekly with cron (Linux/macOS): + +```cron +# Every Sunday at 02:00 local — writes ~/lint-reports/lint-report-YYYY-MM-DD.md +0 2 * * 0 cd /home/you/lint-sweep && /usr/bin/node lint-sweep.mjs \ + --tier=all --max-llm-calls=5 \ + --report=/home/you/lint-reports/lint-report-$(date +\%F).md \ + >> /home/you/lint-reports/lint-sweep.log 2>&1 +``` + +Or with Windows Task Scheduler (`schtasks /create`), systemd timers, a GitHub Actions scheduled workflow, or Supabase `pg_cron` calling a wrapper Edge Function. + +After each run, open the latest `lint-report-YYYY-MM-DD.md`, triage the findings, and act on anything worth fixing via your normal Open Brain tooling. + +## Troubleshooting + +**Issue: `ERROR: OPEN_BRAIN_URL and OPEN_BRAIN_SERVICE_KEY must be set`** +Solution: Create `.env.local` in the same directory as `lint-sweep.mjs` with both variables, or export them in your shell. Fall-through order is `process.env` → `.env.local` → `.env`. + +**Issue: Tier 1 shows `content_fingerprint column missing — see recipes/content-fingerprint-dedup`** +Solution: Your brain predates the [content-fingerprint-dedup](../content-fingerprint-dedup/) primitive. Apply that recipe (and the [fingerprint-dedup-backfill](../fingerprint-dedup-backfill/) recipe) to get duplicate detection. + +**Issue: Tier 2 reports `Graph tables absent`** +Solution: The graph is optional. Install the [ob-graph](../ob-graph/) recipe to create `entities`, `edges`, and `thought_entities`, or run the sweep with `--tier=1,3` equivalent via two invocations. + +**Issue: Tier 3 fails with `OpenRouter HTTP 401`** +Solution: Your `OPENROUTER_API_KEY` is missing, wrong, or out of credit. Verify at https://openrouter.ai/keys. The sweep does not fall back to a different provider on its own. + +**Issue: Report file is written but mostly empty** +Solution: Check the console output — one of the tiers likely short-circuited. Re-run with `--verbose` to see per-call progress. A small brain (<50 thoughts) will produce a short report, which is correct behavior. + +**Issue: LLM call produces unparseable JSON** +Solution: Run with `--verbose` to see the raw response. Switch to a stronger model with `--llm-model=anthropic/claude-sonnet-4-5` if the default Haiku model struggles with your sample. + +## Works Well With + +- **[content-fingerprint-dedup](../content-fingerprint-dedup/)** — installs the `content_fingerprint` column Tier 1 needs for duplicate detection. +- **[fingerprint-dedup-backfill](../fingerprint-dedup-backfill/)** — backfills fingerprints on pre-existing rows so Tier 1 duplicate scanning is accurate. +- **[ob-graph](../ob-graph/)** — installs the `entities`, `edges`, and `thought_entities` tables Tier 2 walks. +- **[thought-enrichment](../thought-enrichment/)** — populates `metadata.topics`, `metadata.tags`, and `metadata.people` so Tier 1 orphan-by-tag detection is meaningful. diff --git a/recipes/lint-sweep/lint-sweep.mjs b/recipes/lint-sweep/lint-sweep.mjs new file mode 100644 index 000000000..7e70ff3b5 --- /dev/null +++ b/recipes/lint-sweep/lint-sweep.mjs @@ -0,0 +1,708 @@ +#!/usr/bin/env node +/** + * lint-sweep.mjs — Bounded weekly brain-quality audit for Open Brain. + * + * Inspired by Karpathy's "lint" concept and the CRATE CLI. Scans the + * `public.thoughts` table for quality issues across three cost tiers: + * + * Tier 1 (SQL-only, free): orphan thoughts, exact/near duplicates, + * low-signal noise, over-tagged soup, content-length outliers. + * + * Tier 2 (graph-based, free): entity-less atomic thoughts, isolated + * clusters in the `edges` table, high-importance with no graph links. + * + * Tier 3 (LLM-assisted, budgeted): contradiction sampling via OpenRouter + * over a small sample of thoughts. Capped by --max-llm-calls. + * + * Produces a markdown report at --report= (default + * ./lint-report-YYYY-MM-DD.md). NEVER mutates the database. Human review + * gates any destructive action — this script only reports. + * + * Usage: + * node lint-sweep.mjs # all three tiers, default caps + * node lint-sweep.mjs --tier=1 # SQL-only sweep + * node lint-sweep.mjs --tier=2 # graph-based sweep + * node lint-sweep.mjs --tier=3 --max-llm-calls=10 # LLM contradiction sampling + * node lint-sweep.mjs --tier=all --sample-size=200 + * node lint-sweep.mjs --report=./out/weekly.md + * + * Environment (loaded from .env or .env.local in the script directory or + * from process.env): + * OPEN_BRAIN_URL — Supabase project URL (e.g., https://xyz.supabase.co) + * OPEN_BRAIN_SERVICE_KEY — Supabase service role key + * OPENROUTER_API_KEY — OpenRouter key (Tier 3 only; omit to skip) + * + * Exit codes: + * 0 — report generated successfully + * 1 — fatal error (missing env, HTTP failure, unparseable response) + */ + +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// ── env loading ───────────────────────────────────────────────────────────── + +function loadEnvFile(envPath) { + if (!fs.existsSync(envPath)) return {}; + const env = {}; + for (const line of fs.readFileSync(envPath, "utf8").split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const eq = trimmed.indexOf("="); + if (eq === -1) continue; + const key = trimmed.slice(0, eq).trim(); + const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, ""); + env[key] = val; + } + return env; +} + +const fileEnv = { + ...loadEnvFile(path.join(__dirname, ".env")), + ...loadEnvFile(path.join(__dirname, ".env.local")), +}; + +function envVar(name) { + return process.env[name] || fileEnv[name] || ""; +} + +// ── args ──────────────────────────────────────────────────────────────────── + +function parseArgs(argv) { + const args = { + tier: "all", // 1 | 2 | 3 | all + sampleSize: 100, // Tier 3 sample size + maxLlmCalls: 5, // Tier 3 hard cap (each call audits ~20 thoughts) + report: null, // output file path (computed below if null) + days: 365, // Tier 3 recency window in days + llmModel: "anthropic/claude-haiku-4-5", + verbose: false, + }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a.startsWith("--tier=")) args.tier = a.slice(7); + else if (a === "--tier") args.tier = argv[++i]; + else if (a.startsWith("--sample-size=")) args.sampleSize = Number(a.slice(14)) || 100; + else if (a === "--sample-size") args.sampleSize = Number(argv[++i]) || 100; + else if (a.startsWith("--max-llm-calls=")) args.maxLlmCalls = Number(a.slice(16)) || 5; + else if (a === "--max-llm-calls") args.maxLlmCalls = Number(argv[++i]) || 5; + else if (a.startsWith("--report=")) args.report = a.slice(9); + else if (a === "--report") args.report = argv[++i]; + else if (a.startsWith("--days=")) args.days = Number(a.slice(7)) || 365; + else if (a === "--days") args.days = Number(argv[++i]) || 365; + else if (a.startsWith("--llm-model=")) args.llmModel = a.slice(12); + else if (a === "--llm-model") args.llmModel = argv[++i]; + else if (a === "--verbose" || a === "-v") args.verbose = true; + else if (a === "--help" || a === "-h") { + console.log(HELP); + process.exit(0); + } + } + if (!["1", "2", "3", "all"].includes(String(args.tier))) { + throw new Error(`Invalid --tier=${args.tier}. Use 1, 2, 3, or all.`); + } + if (args.sampleSize < 1 || args.sampleSize > 1000) { + throw new Error(`--sample-size must be between 1 and 1000 (got ${args.sampleSize}).`); + } + if (args.maxLlmCalls < 0 || args.maxLlmCalls > 100) { + throw new Error(`--max-llm-calls must be between 0 and 100 (got ${args.maxLlmCalls}).`); + } + if (!args.report) { + const date = new Date().toISOString().slice(0, 10); + args.report = path.join(process.cwd(), `lint-report-${date}.md`); + } + return args; +} + +const HELP = ` +lint-sweep.mjs — bounded brain-quality audit for Open Brain + +Usage: node lint-sweep.mjs [options] + +Options: + --tier=<1|2|3|all> Which tier(s) to run (default: all) + --sample-size= Tier 3 sample size in thoughts (default: 100) + --max-llm-calls= Tier 3 hard cap on LLM calls (default: 5) + --report= Markdown report output path + (default: ./lint-report-YYYY-MM-DD.md) + --days= Tier 3 recency window in days (default: 365) + --llm-model= OpenRouter model id + (default: anthropic/claude-haiku-4-5) + --verbose, -v Extra progress output + --help, -h Show this help + +Env (from .env, .env.local, or process.env): + OPEN_BRAIN_URL Supabase project URL + OPEN_BRAIN_SERVICE_KEY Supabase service role key + OPENROUTER_API_KEY OpenRouter key (Tier 3 only) +`.trim(); + +// ── Supabase REST helpers ─────────────────────────────────────────────────── + +function makeRestClient(baseUrl, serviceKey) { + const rest = `${baseUrl.replace(/\/$/, "")}/rest/v1`; + const headers = { + apikey: serviceKey, + Authorization: `Bearer ${serviceKey}`, + "Content-Type": "application/json", + }; + + async function get(pathAndQuery) { + const url = `${rest}${pathAndQuery}`; + const res = await fetch(url, { headers }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`GET ${url} → ${res.status} ${body.slice(0, 300)}`); + } + const text = await res.text(); + return text ? JSON.parse(text) : []; + } + + /** + * Return the exact row count matching `pathAndQuery` (a PostgREST filter + * string starting with `/table?...`). Uses `Prefer: count=exact` and + * parses the Content-Range header. Avoids pulling rows to disk. + */ + async function count(pathAndQuery) { + const url = `${rest}${pathAndQuery}${pathAndQuery.includes("?") ? "&" : "?"}select=id&limit=1`; + const res = await fetch(url, { + headers: { ...headers, Prefer: "count=exact", Range: "0-0" }, + }); + if (!res.ok && res.status !== 206) { + const body = await res.text().catch(() => ""); + throw new Error(`COUNT ${url} → ${res.status} ${body.slice(0, 300)}`); + } + const cr = res.headers.get("content-range") || ""; + const m = cr.match(/\/(\d+|\*)/); + if (m && m[1] !== "*") return Number(m[1]); + const arr = await res.json().catch(() => []); + return Array.isArray(arr) ? arr.length : 0; + } + + async function rpc(fnName, payload) { + const url = `${rest}/rpc/${fnName}`; + const res = await fetch(url, { + method: "POST", + headers, + body: JSON.stringify(payload || {}), + }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`RPC ${fnName} → ${res.status} ${body.slice(0, 300)}`); + } + const text = await res.text(); + return text ? JSON.parse(text) : null; + } + + return { get, count, rpc }; +} + +// ── Tier 1: SQL-only lint (free) ──────────────────────────────────────────── +// +// Each check pulls from `public.thoughts` via PostgREST with aggregation +// done server-side where possible. These are cheap — all run in one second +// against a 100K-thought table. + +async function tier1SqlLint(db, args) { + const out = { + totalThoughts: 0, + orphansByTag: 0, // thoughts with no topics and no tags + exactDuplicates: [], // content_fingerprint collisions (if column exists) + noFingerprint: 0, // rows with NULL content_fingerprint + lowSignalNoise: 0, // importance <= 2 and content short + overTagged: [], // thoughts with >10 tags (usually import noise) + emptyContent: 0, // content IS NULL or trimmed to empty + veryLongContent: 0, // content > 20K chars (usually unchunked dumps) + }; + + // Total row count via Content-Range header — avoids pulling rows + try { + out.totalThoughts = await db.count(`/thoughts`); + } catch { + out.totalThoughts = 0; + } + + // Orphans by tag: metadata.topics and metadata.tags both empty or missing. + // PostgREST can filter JSONB with eq.{} but the safer path is to pull a + // bounded sample and filter in JS. We look at the most recent 2000 rows. + const recent = await db.get( + `/thoughts?select=id,content,created_at,metadata,source_type,importance&order=id.desc&limit=2000` + ); + + for (const t of recent) { + const topics = Array.isArray(t?.metadata?.topics) ? t.metadata.topics : []; + const tags = Array.isArray(t?.metadata?.tags) ? t.metadata.tags : []; + const people = Array.isArray(t?.metadata?.people) ? t.metadata.people : []; + + if (topics.length === 0 && tags.length === 0 && people.length === 0) { + out.orphansByTag++; + } + if (tags.length > 10) { + out.overTagged.push({ id: t.id, tag_count: tags.length }); + } + const content = typeof t.content === "string" ? t.content.trim() : ""; + if (!content) out.emptyContent++; + if (content.length > 20_000) out.veryLongContent++; + const imp = typeof t.importance === "number" ? t.importance : null; + if (imp !== null && imp <= 2 && content.length < 40) out.lowSignalNoise++; + } + + // Exact duplicates — only meaningful if content_fingerprint is populated + try { + const fp = await db.get( + `/thoughts?select=id,content_fingerprint&content_fingerprint=not.is.null&order=content_fingerprint.asc&limit=5000` + ); + const buckets = new Map(); + for (const row of fp) { + if (!row.content_fingerprint) continue; + const list = buckets.get(row.content_fingerprint) || []; + list.push(row.id); + buckets.set(row.content_fingerprint, list); + } + for (const [fingerprint, ids] of buckets) { + if (ids.length > 1) { + out.exactDuplicates.push({ fingerprint: fingerprint.slice(0, 12), ids: ids.slice(0, 5), copies: ids.length }); + } + } + } catch (e) { + // column may not exist on this brain; that's fine — report it + out.exactDuplicates = [{ note: "content_fingerprint column missing — see recipes/content-fingerprint-dedup" }]; + } + + try { + out.noFingerprint = await db.count(`/thoughts?content_fingerprint=is.null`); + } catch { + // column missing — already flagged above + out.noFingerprint = 0; + } + + return out; +} + +// ── Tier 2: graph-based lint (free) ───────────────────────────────────────── +// +// Looks at the knowledge graph (entities, edges, thought_entities) to find +// structural issues: high-importance thoughts with no entity links, entities +// with zero edges (isolated nodes), edges pointing to non-existent thoughts. +// +// All reads — no LLM, no writes. + +async function tier2GraphLint(db, args) { + const out = { + highImportanceIsolated: [], // importance >= 4 thoughts with no entity links + entitiesWithNoEdges: 0, + thoughtEntityDanglingThought: 0, // thought_entities rows whose thought id is gone + thoughtEntityDanglingEntity: 0, // thought_entities rows whose entity id is gone + graphTablesMissing: [], // which of (entities, edges, thought_entities) are absent + }; + + // Probe graph tables — they're optional in Open Brain + for (const t of ["entities", "edges", "thought_entities"]) { + try { + await db.get(`/${t}?select=*&limit=1`); + } catch { + out.graphTablesMissing.push(t); + } + } + if (out.graphTablesMissing.length === 3) { + return out; // no graph tables at all — skip tier silently + } + + // High-importance thoughts with no rows in thought_entities + if (!out.graphTablesMissing.includes("thought_entities")) { + const hi = await db.get( + `/thoughts?select=id,content,importance,created_at&importance=gte.4&order=id.desc&limit=500` + ); + // Fetch up to 500 thought_entities rows keyed on those ids. + const ids = hi.map((r) => r.id).slice(0, 500); + if (ids.length > 0) { + // PostgREST in(...) filter — cap at 100 ids per request. + const linked = new Set(); + for (let i = 0; i < ids.length; i += 100) { + const chunk = ids.slice(i, i + 100); + const rows = await db.get( + `/thought_entities?select=thought_id&thought_id=in.(${chunk.join(",")})` + ); + for (const r of rows) linked.add(r.thought_id); + } + for (const t of hi) { + if (!linked.has(t.id)) { + out.highImportanceIsolated.push({ + id: t.id, + importance: t.importance, + created_at: t.created_at, + preview: String(t.content || "").slice(0, 120), + }); + } + } + } + } + + // Entities with zero edges + if (!out.graphTablesMissing.includes("entities") && !out.graphTablesMissing.includes("edges")) { + const ents = await db.get(`/entities?select=id&limit=2000`); + const edges = await db.get(`/edges?select=src_entity_id,dst_entity_id&limit=5000`); + const touched = new Set(); + for (const e of edges) { + if (e.src_entity_id != null) touched.add(e.src_entity_id); + if (e.dst_entity_id != null) touched.add(e.dst_entity_id); + } + out.entitiesWithNoEdges = ents.filter((e) => !touched.has(e.id)).length; + } + + return out; +} + +// ── Tier 3: LLM-assisted contradiction sampling (budgeted) ────────────────── +// +// Samples N thoughts, groups them into batches of ~20, sends each batch to +// OpenRouter once. Each batch produces findings across six categories. +// Total LLM calls ≤ --max-llm-calls (default 5 → audits 100 thoughts). + +async function tier3LlmLint(db, args) { + const out = { + enabled: true, + skippedReason: null, + llmCalls: 0, + sampleSize: 0, + findings: { + contradictions: [], + stale_facts: [], + superseded: [], + orphans: [], + low_signal: [], + missing_links: [], + }, + }; + + const openrouterKey = envVar("OPENROUTER_API_KEY"); + if (!openrouterKey) { + out.enabled = false; + out.skippedReason = "OPENROUTER_API_KEY not set"; + return out; + } + if (args.maxLlmCalls === 0) { + out.enabled = false; + out.skippedReason = "--max-llm-calls=0"; + return out; + } + + // Pull a recent sample of atomic (non-derived) thoughts + const since = new Date(Date.now() - args.days * 86_400_000).toISOString(); + const rows = await db.get( + `/thoughts?select=id,content,importance,type,source_type,created_at,metadata` + + `&created_at=gte.${encodeURIComponent(since)}` + + `&order=id.desc&limit=${Math.min(args.sampleSize * 2, 500)}` + ); + const atomic = rows.filter( + (t) => t?.metadata?.derivation_layer !== "derived" && typeof t.content === "string" && t.content.trim().length >= 20 + ); + const sample = atomic.slice(0, args.sampleSize); + out.sampleSize = sample.length; + + if (sample.length < 10) { + out.enabled = false; + out.skippedReason = `only ${sample.length} eligible thoughts in last ${args.days} days (need 10)`; + return out; + } + + const BATCH = 20; + const batches = []; + for (let i = 0; i < sample.length; i += BATCH) { + if (batches.length >= args.maxLlmCalls) break; + batches.push(sample.slice(i, i + BATCH)); + } + + for (const batch of batches) { + const rowsForPrompt = batch.map((t) => ({ + id: t.id, + date: String(t.created_at || "").slice(0, 10), + type: t.type, + importance: t.importance, + content: String(t.content || "").slice(0, 400), + topics: (t?.metadata?.topics ?? []).slice(0, 5), + tags: (t?.metadata?.tags ?? []).slice(0, 5), + })); + + const systemPrompt = + "You audit a personal second brain for quality issues. " + + "Given a cluster of thoughts, identify GENUINE problems across six categories. " + + "Be CONSERVATIVE — only flag real issues, never stylistic preferences or minor overlap. " + + "If a category has no issues, return an empty array. " + + "Output STRICT valid JSON with this exact shape:\n" + + "{\n" + + ' "contradictions": [{"thought_ids": [N,M], "issue": "...", "suggested_action": "..."}],\n' + + ' "stale_facts": [{"thought_ids": [N], "issue": "...", "suggested_action": "..."}],\n' + + ' "superseded": [{"thought_ids": [N,M], "issue": "...", "suggested_action": "..."}],\n' + + ' "orphans": [{"thought_ids": [N], "issue": "...", "suggested_action": "..."}],\n' + + ' "low_signal": [{"thought_ids": [N], "issue": "...", "suggested_action": "..."}],\n' + + ' "missing_links": [{"thought_ids": [N,M], "issue": "...", "suggested_action": "..."}]\n' + + "}\n" + + "No markdown, no commentary, JSON only. " + + "Definitions:\n" + + " contradictions — two thoughts state incompatible facts.\n" + + " stale_facts — deadlines passed, tech deprecated, statuses outdated.\n" + + " superseded — older decision replaced by newer one but not marked.\n" + + " orphans — content has no natural connection to anything else in sample.\n" + + " low_signal — importance >= 4 but content is trivial.\n" + + " missing_links — two thoughts about the same subject not cross-referenced."; + + const userPrompt = + `Sample size: ${rowsForPrompt.length}\n\n` + + `THOUGHTS:\n${JSON.stringify(rowsForPrompt)}\n\n` + + `Audit the cluster now. JSON output only.`; + + if (args.verbose) { + console.error(` [tier3] LLM call ${out.llmCalls + 1}/${batches.length} (${rowsForPrompt.length} thoughts)`); + } + + const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${openrouterKey}`, + "Content-Type": "application/json", + "HTTP-Referer": "https://github.com/NateBJones-Projects/OB1", + "X-Title": "Open Brain Lint Sweep", + }, + body: JSON.stringify({ + model: args.llmModel, + max_tokens: 2048, + temperature: 0, + response_format: { type: "json_object" }, + messages: [ + { role: "system", content: systemPrompt }, + { role: "user", content: userPrompt }, + ], + }), + }); + + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`OpenRouter HTTP ${res.status}: ${body.slice(0, 300)}`); + } + + const payload = await res.json(); + const raw = payload?.choices?.[0]?.message?.content?.trim() ?? ""; + out.llmCalls++; + + const cleaned = raw.replace(/^```(?:json)?/m, "").replace(/```$/m, "").trim(); + let parsed; + try { + parsed = JSON.parse(cleaned); + } catch (e) { + throw new Error(`Failed to parse Tier 3 JSON (call ${out.llmCalls}): ${e.message}\nRaw: ${raw.slice(0, 300)}`); + } + + for (const cat of Object.keys(out.findings)) { + const arr = Array.isArray(parsed?.[cat]) ? parsed[cat] : []; + out.findings[cat].push(...arr); + } + } + + return out; +} + +// ── report rendering ──────────────────────────────────────────────────────── + +function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { + const lines = []; + const now = new Date().toISOString(); + lines.push("---"); + lines.push(`title: Lint Sweep — ${now.slice(0, 10)}`); + lines.push(`generated_at: ${now}`); + lines.push(`tier: ${args.tier}`); + lines.push(`started_at: ${startedAt}`); + lines.push(`finished_at: ${finishedAt}`); + lines.push("---"); + lines.push(""); + lines.push(`# Open Brain Lint Sweep — ${now.slice(0, 10)}`); + lines.push(""); + lines.push("*Read-only audit. This script never mutates thoughts.*"); + lines.push(""); + + // Summary header + const tier3Count = tier3 + ? Object.values(tier3.findings || {}).reduce((n, a) => n + (Array.isArray(a) ? a.length : 0), 0) + : 0; + lines.push("## Summary"); + lines.push(""); + if (tier1) { + lines.push(`- Total thoughts inspected (Tier 1 sample): ${tier1.totalThoughts}`); + lines.push(`- Orphans by tag (recent 2000): ${tier1.orphansByTag}`); + lines.push(`- Exact-duplicate fingerprint groups: ${Array.isArray(tier1.exactDuplicates) ? tier1.exactDuplicates.filter((x) => x.ids).length : 0}`); + lines.push(`- Rows missing content_fingerprint: ${tier1.noFingerprint}`); + lines.push(`- Low-signal noise candidates: ${tier1.lowSignalNoise}`); + } + if (tier2) { + lines.push(`- High-importance isolated (no entity links): ${tier2.highImportanceIsolated.length}`); + lines.push(`- Entities with zero edges: ${tier2.entitiesWithNoEdges}`); + } + if (tier3) { + if (tier3.enabled) { + lines.push(`- LLM contradiction findings: ${tier3Count} (over ${tier3.sampleSize} thoughts, ${tier3.llmCalls} LLM calls)`); + } else { + lines.push(`- Tier 3 skipped: ${tier3.skippedReason}`); + } + } + lines.push(""); + + // Tier 1 details + if (tier1) { + lines.push("## Tier 1 — SQL-only lint (free)"); + lines.push(""); + lines.push(`- Orphans by tag (recent 2000 thoughts): **${tier1.orphansByTag}** — thoughts with no topics, tags, or people.`); + lines.push(`- Over-tagged (>10 tags): **${tier1.overTagged.length}** — typically import noise.`); + if (tier1.overTagged.length > 0) { + for (const row of tier1.overTagged.slice(0, 10)) { + lines.push(` - thought #${row.id} → ${row.tag_count} tags`); + } + if (tier1.overTagged.length > 10) lines.push(` - …and ${tier1.overTagged.length - 10} more`); + } + lines.push(`- Empty content: **${tier1.emptyContent}**`); + lines.push(`- Very long content (>20K chars): **${tier1.veryLongContent}** — usually unchunked dumps.`); + lines.push(`- Low-signal noise (importance ≤2, content <40 chars): **${tier1.lowSignalNoise}**`); + lines.push(`- Exact-duplicate fingerprint groups: **${Array.isArray(tier1.exactDuplicates) ? tier1.exactDuplicates.filter((x) => x.ids).length : 0}**`); + if (Array.isArray(tier1.exactDuplicates)) { + for (const d of tier1.exactDuplicates.slice(0, 10)) { + if (d.note) lines.push(` - *${d.note}*`); + else lines.push(` - fingerprint ${d.fingerprint}… → ${d.copies} copies (ids: ${d.ids.join(", ")})`); + } + } + lines.push(`- Rows missing content_fingerprint: **${tier1.noFingerprint}** — consider running the fingerprint-dedup-backfill recipe.`); + lines.push(""); + } + + // Tier 2 details + if (tier2) { + lines.push("## Tier 2 — Graph-based lint (free)"); + lines.push(""); + if (tier2.graphTablesMissing.length > 0) { + lines.push(`*Graph tables absent: ${tier2.graphTablesMissing.join(", ")}. Install \`recipes/ob-graph\` first for full Tier 2 coverage.*`); + lines.push(""); + } + lines.push(`- High-importance (≥4) thoughts with no entity links: **${tier2.highImportanceIsolated.length}**`); + for (const row of tier2.highImportanceIsolated.slice(0, 15)) { + lines.push(` - #${row.id} (imp=${row.importance}, ${row.created_at?.slice(0, 10)}) — ${row.preview}${row.preview.length >= 120 ? "…" : ""}`); + } + if (tier2.highImportanceIsolated.length > 15) { + lines.push(` - …and ${tier2.highImportanceIsolated.length - 15} more`); + } + lines.push(`- Entities with zero edges: **${tier2.entitiesWithNoEdges}**`); + lines.push(""); + } + + // Tier 3 details + if (tier3) { + lines.push("## Tier 3 — LLM-assisted contradiction sampling (budgeted)"); + lines.push(""); + if (!tier3.enabled) { + lines.push(`*Skipped: ${tier3.skippedReason}.*`); + lines.push(""); + } else { + lines.push(`- Sample size: **${tier3.sampleSize}** thoughts`); + lines.push(`- LLM calls: **${tier3.llmCalls}** (cap: ${args.maxLlmCalls})`); + lines.push(`- Model: \`${args.llmModel}\``); + lines.push(""); + + const sections = [ + ["Contradictions", tier3.findings.contradictions, "Two thoughts state incompatible facts."], + ["Stale Facts", tier3.findings.stale_facts, "Deadlines, statuses, or tech references that appear outdated."], + ["Superseded Decisions", tier3.findings.superseded, "Older decisions replaced by newer ones but not marked."], + ["Orphan Content", tier3.findings.orphans, "Thoughts with no natural connection to the rest of the sample."], + ["Low-Signal (importance ≥4 but trivial)", tier3.findings.low_signal, "Thoughts rated important that don't carry weight."], + ["Missing-Link Suggestions", tier3.findings.missing_links, "Thoughts about the same subject that should cross-reference."], + ]; + for (const [heading, items, subtitle] of sections) { + lines.push(`### ${heading} (${items.length})`); + lines.push(""); + lines.push(`*${subtitle}*`); + lines.push(""); + if (items.length === 0) { + lines.push("- none"); + } else { + for (const item of items) { + const ids = (item.thought_ids ?? []).map((i) => `#${i}`).join(", "); + lines.push(`- **${ids}** — ${item.issue}`); + if (item.suggested_action) lines.push(` - *Action:* ${item.suggested_action}`); + } + } + lines.push(""); + } + } + } + + lines.push("---"); + lines.push(""); + lines.push("**Safety:** `lint-sweep.mjs` is read-only. Every finding above is a suggestion for a human to review. "); + lines.push("Before acting on any item, verify the thought with `get_thought` or the web UI. "); + lines.push("Never delete or edit a thought based solely on this report."); + return lines.join("\n"); +} + +// ── main ──────────────────────────────────────────────────────────────────── + +async function main() { + const args = parseArgs(process.argv.slice(2)); + + const baseUrl = envVar("OPEN_BRAIN_URL"); + const serviceKey = envVar("OPEN_BRAIN_SERVICE_KEY"); + if (!baseUrl || !serviceKey) { + console.error("ERROR: OPEN_BRAIN_URL and OPEN_BRAIN_SERVICE_KEY must be set (env or .env.local)."); + process.exit(1); + } + const db = makeRestClient(baseUrl, serviceKey); + + const startedAt = new Date().toISOString(); + console.log(`[lint-sweep] tier=${args.tier} sample=${args.sampleSize} max_llm_calls=${args.maxLlmCalls} report=${args.report}`); + + let tier1 = null; + let tier2 = null; + let tier3 = null; + + if (args.tier === "1" || args.tier === "all") { + console.log("[tier 1] SQL-only lint…"); + tier1 = await tier1SqlLint(db, args); + console.log( + `[tier 1] done — ${tier1.totalThoughts} total thoughts, ` + + `${tier1.orphansByTag} orphans-by-tag, ` + + `${Array.isArray(tier1.exactDuplicates) ? tier1.exactDuplicates.filter((x) => x.ids).length : 0} dup groups, ` + + `${tier1.noFingerprint} missing-fingerprint` + ); + } + + if (args.tier === "2" || args.tier === "all") { + console.log("[tier 2] graph lint…"); + tier2 = await tier2GraphLint(db, args); + console.log( + `[tier 2] done — ${tier2.highImportanceIsolated.length} high-imp isolated, ` + + `${tier2.entitiesWithNoEdges} isolated entities` + + (tier2.graphTablesMissing.length ? `, missing: ${tier2.graphTablesMissing.join(",")}` : "") + ); + } + + if (args.tier === "3" || args.tier === "all") { + console.log("[tier 3] LLM contradiction sampling…"); + tier3 = await tier3LlmLint(db, args); + if (tier3.enabled) { + const total = Object.values(tier3.findings).reduce((n, a) => n + a.length, 0); + console.log(`[tier 3] done — ${total} findings over ${tier3.sampleSize} thoughts (${tier3.llmCalls} LLM calls)`); + } else { + console.log(`[tier 3] skipped — ${tier3.skippedReason}`); + } + } + + const finishedAt = new Date().toISOString(); + const report = renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }); + fs.mkdirSync(path.dirname(path.resolve(args.report)), { recursive: true }); + fs.writeFileSync(args.report, report, "utf8"); + console.log(`[lint-sweep] report written → ${args.report}`); +} + +main().catch((err) => { + console.error("[lint-sweep] FAILED:", err?.message || err); + if (process.env.DEBUG) console.error(err); + process.exit(1); +}); diff --git a/recipes/lint-sweep/metadata.json b/recipes/lint-sweep/metadata.json new file mode 100644 index 000000000..16ac5851b --- /dev/null +++ b/recipes/lint-sweep/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Lint Sweep — bounded brain quality audit", + "description": "Weekly quality audit across three cost tiers: SQL-only lint (orphans, duplicates, low-signal noise), graph-based lint (isolated high-importance thoughts, orphan entities), and LLM-assisted contradiction sampling (budget-capped). Read-only — produces a markdown report, never mutates thoughts.", + "category": "recipes", + "version": "1.0.0", + "author": { + "name": "Alan Shurafa", + "github": "alanshurafa" + }, + "requires": { + "open_brain": true, + "services": ["OpenRouter (Tier 3 only)"], + "tools": ["Node.js 18+"] + }, + "tags": ["quality", "audit", "maintenance", "contradictions", "orphans", "staleness"], + "difficulty": "intermediate", + "estimated_time": "30 minutes", + "created": "2026-04-17", + "updated": "2026-04-17" +} diff --git a/recipes/lint-sweep/views.sql b/recipes/lint-sweep/views.sql new file mode 100644 index 000000000..1b2a2b686 --- /dev/null +++ b/recipes/lint-sweep/views.sql @@ -0,0 +1,138 @@ +-- lint-sweep views.sql +-- +-- OPTIONAL: SQL views that let you run the Tier 1 checks directly in +-- Supabase Studio or psql without installing the Node.js script. Apply +-- once; they become queryable read-only views over `public.thoughts` +-- (and optionally `public.entities` / `public.thought_entities`). +-- +-- Safety: VIEWS ONLY. No destructive DDL, no data modification. Run in +-- the Supabase SQL editor as the project owner. To remove a view later, +-- use "DROP VIEW IF EXISTS CASCADE;" from the SQL editor. + +-- 1. Orphans by tag — thoughts with no topics, tags, or people in metadata +CREATE OR REPLACE VIEW lint_orphans_by_tag AS +SELECT + id, + created_at, + importance, + left(content, 160) AS preview, + source_type +FROM public.thoughts +WHERE COALESCE(jsonb_array_length(metadata->'topics'), 0) = 0 + AND COALESCE(jsonb_array_length(metadata->'tags'), 0) = 0 + AND COALESCE(jsonb_array_length(metadata->'people'), 0) = 0 +ORDER BY id DESC; + +COMMENT ON VIEW lint_orphans_by_tag IS + 'Lint: thoughts with no topics/tags/people tags in metadata.'; + +-- 2. Over-tagged thoughts — usually import noise +CREATE OR REPLACE VIEW lint_over_tagged AS +SELECT + id, + created_at, + jsonb_array_length(metadata->'tags') AS tag_count, + left(content, 160) AS preview +FROM public.thoughts +WHERE COALESCE(jsonb_array_length(metadata->'tags'), 0) > 10 +ORDER BY tag_count DESC; + +COMMENT ON VIEW lint_over_tagged IS + 'Lint: thoughts with more than 10 tags — commonly import noise.'; + +-- 3. Empty-content thoughts — captured but never populated +CREATE OR REPLACE VIEW lint_empty_content AS +SELECT id, created_at, source_type, importance +FROM public.thoughts +WHERE content IS NULL + OR btrim(content) = '' +ORDER BY id DESC; + +COMMENT ON VIEW lint_empty_content IS 'Lint: thoughts with empty content.'; + +-- 4. Very long content — usually unchunked dumps +CREATE OR REPLACE VIEW lint_very_long AS +SELECT + id, + created_at, + length(content) AS chars, + left(content, 200) AS preview +FROM public.thoughts +WHERE length(content) > 20000 +ORDER BY chars DESC; + +COMMENT ON VIEW lint_very_long IS + 'Lint: thoughts over 20k characters — usually unchunked dumps.'; + +-- 5. Low-signal noise — importance <= 2 and content under 40 chars +CREATE OR REPLACE VIEW lint_low_signal AS +SELECT id, created_at, importance, content +FROM public.thoughts +WHERE importance IS NOT NULL + AND importance <= 2 + AND length(COALESCE(btrim(content), '')) < 40 +ORDER BY id DESC; + +COMMENT ON VIEW lint_low_signal IS + 'Lint: importance <= 2 AND content under 40 chars.'; + +-- 6. Duplicate fingerprint groups — only meaningful if content_fingerprint +-- is populated (see recipes/content-fingerprint-dedup). Safe to create +-- even if the column is missing — the view definition will fail, but +-- you can skip this one. +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'content_fingerprint' + ) THEN + EXECUTE $v$ + CREATE OR REPLACE VIEW lint_exact_duplicates AS + SELECT + content_fingerprint, + count(*) AS copies, + array_agg(id ORDER BY id) AS ids + FROM public.thoughts + WHERE content_fingerprint IS NOT NULL + GROUP BY content_fingerprint + HAVING count(*) > 1 + ORDER BY copies DESC; + + COMMENT ON VIEW lint_exact_duplicates IS + 'Lint: fingerprint collisions — rows with identical content_fingerprint.'; + $v$; + END IF; +END $$; + +-- 7. High-importance thoughts with no graph links. Requires the +-- public.thought_entities table (see recipes/ob-graph). Skipped +-- silently when missing. +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'thought_entities' + ) THEN + EXECUTE $v$ + CREATE OR REPLACE VIEW lint_high_importance_isolated AS + SELECT + t.id, + t.created_at, + t.importance, + left(t.content, 200) AS preview + FROM public.thoughts t + LEFT JOIN public.thought_entities te ON te.thought_id = t.id + WHERE t.importance >= 4 + AND te.thought_id IS NULL + ORDER BY t.importance DESC, t.id DESC; + + COMMENT ON VIEW lint_high_importance_isolated IS + 'Lint: importance >= 4 thoughts with zero entity links.'; + $v$; + END IF; +END $$; From f6de122a45c81a6cd64a164ffc85d093bcc070d7 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:21:10 -0400 Subject: [PATCH 004/125] [recipes] Fix REVIEW-CODEX-P1-1: --max-llm-calls=0 now actually disables LLM --- recipes/lint-sweep/lint-sweep.mjs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/recipes/lint-sweep/lint-sweep.mjs b/recipes/lint-sweep/lint-sweep.mjs index 7e70ff3b5..19c5145de 100644 --- a/recipes/lint-sweep/lint-sweep.mjs +++ b/recipes/lint-sweep/lint-sweep.mjs @@ -81,18 +81,30 @@ function parseArgs(argv) { llmModel: "anthropic/claude-haiku-4-5", verbose: false, }; + + // Parse a numeric flag value WITHOUT rewriting valid zero/negative values. + // Plain `Number(x) || default` fails for 0 — e.g. `--max-llm-calls=0` (the + // documented hard-off switch for Tier 3) would be silently rewritten to the + // default, still making paid LLM calls. This helper only applies the default + // when the parsed value is genuinely missing or non-numeric. + function parseNumberFlag(raw, defaultValue) { + if (raw === undefined || raw === null || raw === "") return defaultValue; + const n = Number(raw); + return Number.isFinite(n) ? n : defaultValue; + } + for (let i = 0; i < argv.length; i++) { const a = argv[i]; if (a.startsWith("--tier=")) args.tier = a.slice(7); else if (a === "--tier") args.tier = argv[++i]; - else if (a.startsWith("--sample-size=")) args.sampleSize = Number(a.slice(14)) || 100; - else if (a === "--sample-size") args.sampleSize = Number(argv[++i]) || 100; - else if (a.startsWith("--max-llm-calls=")) args.maxLlmCalls = Number(a.slice(16)) || 5; - else if (a === "--max-llm-calls") args.maxLlmCalls = Number(argv[++i]) || 5; + else if (a.startsWith("--sample-size=")) args.sampleSize = parseNumberFlag(a.slice(14), 100); + else if (a === "--sample-size") args.sampleSize = parseNumberFlag(argv[++i], 100); + else if (a.startsWith("--max-llm-calls=")) args.maxLlmCalls = parseNumberFlag(a.slice(16), 5); + else if (a === "--max-llm-calls") args.maxLlmCalls = parseNumberFlag(argv[++i], 5); else if (a.startsWith("--report=")) args.report = a.slice(9); else if (a === "--report") args.report = argv[++i]; - else if (a.startsWith("--days=")) args.days = Number(a.slice(7)) || 365; - else if (a === "--days") args.days = Number(argv[++i]) || 365; + else if (a.startsWith("--days=")) args.days = parseNumberFlag(a.slice(7), 365); + else if (a === "--days") args.days = parseNumberFlag(argv[++i], 365); else if (a.startsWith("--llm-model=")) args.llmModel = a.slice(12); else if (a === "--llm-model") args.llmModel = argv[++i]; else if (a === "--verbose" || a === "-v") args.verbose = true; From 4262879ea6c0276797733884c50aceb1eb4f10de Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:22:15 -0400 Subject: [PATCH 005/125] [recipes] Fix REVIEW-CODEX-P1-2: document entity-extraction (not ob-graph) as Tier 2 prereq --- recipes/lint-sweep/README.md | 11 +++++++---- recipes/lint-sweep/lint-sweep.mjs | 2 +- recipes/lint-sweep/views.sql | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index a411142e9..0fa396dd7 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -13,7 +13,7 @@ Inspired by Andrej Karpathy's "lint the wiki" pattern and the [CRATE CLI](https: | Tier | What it checks | Cost | Runs for | | ---- | -------------- | ---- | -------- | | **1 — SQL-only** | Orphans by tag, exact fingerprint duplicates, missing fingerprints, low-signal noise, over-tagged thoughts, empty content, unchunked dumps | $0 (no LLM) | Any brain, any size | -| **2 — Graph-based** | High-importance thoughts with no entity links, entities with zero edges | $0 (no LLM) | Brains that have the [ob-graph](../ob-graph/) recipe applied | +| **2 — Graph-based** | High-importance thoughts with no entity links, entities with zero edges | $0 (no LLM) | Brains that have the `entity-extraction` schema applied (ships `entities`, `edges`, `thought_entities` tables) | | **3 — LLM-assisted** | Semantic contradictions, stale facts, superseded decisions, missing-link suggestions, orphan content, low-signal despite high importance | ~$0.01–0.05 per 100 thoughts sampled (Claude Haiku via OpenRouter) | Brains with OpenRouter key configured | Tier 1 and Tier 2 run against your Supabase project via PostgREST and complete in seconds against a 100K-thought brain. Tier 3 is the only tier that calls an external API — and it is hard-capped by `--max-llm-calls`. @@ -22,9 +22,12 @@ Tier 1 and Tier 2 run against your Supabase project via PostgREST and complete i - Working [Open Brain setup](../../docs/01-getting-started.md) with `public.thoughts` populated - Node.js 18 or later -- (Optional, Tier 2) [ob-graph](../ob-graph/) recipe applied for graph-based lint +- (Optional, Tier 2) The `entity-extraction` schema applied (ships the `entities`, `edges`, and `thought_entities` tables Tier 2 walks). If your brain was set up before that schema landed, see the schema PRs [#197](https://github.com/NateBJones-Projects/OB1/pull/197) and [#199](https://github.com/NateBJones-Projects/OB1/pull/199). Tier 2 is skipped gracefully when these tables are absent — it does NOT use the `ob-graph` recipe's `graph_nodes` / `graph_edges` tables. - (Optional, Tier 3) An OpenRouter API key with credit available +> [!IMPORTANT] +> Tier 2 depends on the `entity-extraction` schema (`entities`, `edges`, `thought_entities`), not the `ob-graph` recipe. `ob-graph` creates differently-named tables (`graph_nodes`, `graph_edges`) and is not compatible with Tier 2. If you only have `ob-graph` installed, Tier 2 will log the tables as missing and skip. + ## Credential Tracker Copy this block into a text editor and fill it in as you go. @@ -244,7 +247,7 @@ Solution: Create `.env.local` in the same directory as `lint-sweep.mjs` with bot Solution: Your brain predates the [content-fingerprint-dedup](../content-fingerprint-dedup/) primitive. Apply that recipe (and the [fingerprint-dedup-backfill](../fingerprint-dedup-backfill/) recipe) to get duplicate detection. **Issue: Tier 2 reports `Graph tables absent`** -Solution: The graph is optional. Install the [ob-graph](../ob-graph/) recipe to create `entities`, `edges`, and `thought_entities`, or run the sweep with `--tier=1,3` equivalent via two invocations. +Solution: Tier 2 requires the `entity-extraction` schema (`entities`, `edges`, `thought_entities`). If your brain predates that schema, see PRs [#197](https://github.com/NateBJones-Projects/OB1/pull/197) and [#199](https://github.com/NateBJones-Projects/OB1/pull/199), or skip Tier 2 entirely by running `--tier=1` and `--tier=3` separately. Note: the `ob-graph` recipe uses different table names (`graph_nodes`, `graph_edges`) and does NOT satisfy this dependency. **Issue: Tier 3 fails with `OpenRouter HTTP 401`** Solution: Your `OPENROUTER_API_KEY` is missing, wrong, or out of credit. Verify at https://openrouter.ai/keys. The sweep does not fall back to a different provider on its own. @@ -259,5 +262,5 @@ Solution: Run with `--verbose` to see the raw response. Switch to a stronger mod - **[content-fingerprint-dedup](../content-fingerprint-dedup/)** — installs the `content_fingerprint` column Tier 1 needs for duplicate detection. - **[fingerprint-dedup-backfill](../fingerprint-dedup-backfill/)** — backfills fingerprints on pre-existing rows so Tier 1 duplicate scanning is accurate. -- **[ob-graph](../ob-graph/)** — installs the `entities`, `edges`, and `thought_entities` tables Tier 2 walks. +- **`entity-extraction` schema** — installs the `entities`, `edges`, and `thought_entities` tables Tier 2 walks. See PRs [#197](https://github.com/NateBJones-Projects/OB1/pull/197) and [#199](https://github.com/NateBJones-Projects/OB1/pull/199). (The `ob-graph` recipe is a separate build with different table names and is NOT compatible with Tier 2.) - **[thought-enrichment](../thought-enrichment/)** — populates `metadata.topics`, `metadata.tags`, and `metadata.people` so Tier 1 orphan-by-tag detection is meaningful. diff --git a/recipes/lint-sweep/lint-sweep.mjs b/recipes/lint-sweep/lint-sweep.mjs index 19c5145de..fe1d09887 100644 --- a/recipes/lint-sweep/lint-sweep.mjs +++ b/recipes/lint-sweep/lint-sweep.mjs @@ -592,7 +592,7 @@ function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { lines.push("## Tier 2 — Graph-based lint (free)"); lines.push(""); if (tier2.graphTablesMissing.length > 0) { - lines.push(`*Graph tables absent: ${tier2.graphTablesMissing.join(", ")}. Install \`recipes/ob-graph\` first for full Tier 2 coverage.*`); + lines.push(`*Graph tables absent: ${tier2.graphTablesMissing.join(", ")}. Tier 2 requires the \`entity-extraction\` schema (which ships \`entities\`, \`edges\`, \`thought_entities\`) — see PRs #197 and #199. The \`ob-graph\` recipe uses different table names and does NOT satisfy this dependency.*`); lines.push(""); } lines.push(`- High-importance (≥4) thoughts with no entity links: **${tier2.highImportanceIsolated.length}**`); diff --git a/recipes/lint-sweep/views.sql b/recipes/lint-sweep/views.sql index 1b2a2b686..89ab3e923 100644 --- a/recipes/lint-sweep/views.sql +++ b/recipes/lint-sweep/views.sql @@ -108,8 +108,10 @@ BEGIN END $$; -- 7. High-importance thoughts with no graph links. Requires the --- public.thought_entities table (see recipes/ob-graph). Skipped --- silently when missing. +-- public.thought_entities table from the `entity-extraction` schema +-- (see PRs #197 and #199), NOT the `ob-graph` recipe (which uses +-- different table names: graph_nodes / graph_edges). Skipped silently +-- when `thought_entities` is missing. DO $$ BEGIN IF EXISTS ( From 9575378169bd1bd3e093bcec03ebeb2747c74608 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:22:22 -0400 Subject: [PATCH 006/125] [recipes] Fix REVIEW-CODEX-P1-2: sensitivity fallback now fails closed --- recipes/weekly-digest/weekly-digest.mjs | 73 +++++++++++++++++++------ 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 34ff99004..4ecd444cd 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -68,14 +68,18 @@ function parseArgs(argv) { minImportance: 4, model: DEFAULT_MODEL, output: "telegram", + outputExplicit: false, includePersonal: false, dryRun: false, + noSensitivityFilter: false, }; for (const raw of argv) { if (raw === "--dry-run") { args.dryRun = true; } else if (raw === "--include-personal") { args.includePersonal = true; + } else if (raw === "--no-sensitivity-filter") { + args.noSensitivityFilter = true; } else if (raw.startsWith("--window=")) { const n = Number(raw.slice("--window=".length)); if (!Number.isFinite(n) || n <= 0) { @@ -97,6 +101,7 @@ function parseArgs(argv) { throw new Error(`--output must be telegram|stdout|file, got: ${val}`); } args.output = val; + args.outputExplicit = true; } else if (raw === "--help" || raw === "-h") { printHelp(); process.exit(0); @@ -121,6 +126,9 @@ function printHelp() { " Aliases: opus, sonnet, haiku", " --output= telegram | stdout | file (default: telegram)", " --include-personal Include sensitivity_tier=personal thoughts", + " --no-sensitivity-filter UNSAFE: run without sensitivity_tier filter.", + " Use only when you accept that restricted/personal", + " thoughts will be sent to the LLM + delivery target.", " --dry-run Synthesize + print, deliver nothing", " -h, --help Show this help", ].join("\n"), @@ -183,15 +191,34 @@ function loadConfig(args) { * this recipe runnable on a stock Open Brain without requiring a custom * REST gateway edge function. */ -async function fetchThoughts(cfg, { windowDays, includePersonal }) { +async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFilter }) { const sinceIso = new Date(Date.now() - windowDays * 86_400_000).toISOString(); // Build the sensitivity exclusion list. sensitivity_tier is a TEXT column // added by the sensitivity-tiers primitive; if a given Open Brain install - // doesn't have it, PostgREST will 400 and we fall back to a query with no - // sensitivity filter (see fallback below). + // doesn't have it, PostgREST will 400 and we FAIL CLOSED by default to + // protect the privacy boundary the README promises ("restricted never + // leaves the database"). The user can opt out with --no-sensitivity-filter + // if they accept the leakage risk and explicitly want to run unfiltered. const excluded = includePersonal ? ["restricted"] : ["restricted", "personal"]; - const excludeFilter = `&sensitivity_tier=not.in.(${excluded.join(",")})`; + // When --no-sensitivity-filter is set, skip the filter entirely so a missing + // column won't trip the 400. Otherwise, apply the exclusion filter. + const selectCols = noSensitivityFilter + ? "id,content,created_at,importance,metadata" + : "id,content,created_at,importance,metadata,sensitivity_tier"; + const excludeFilter = noSensitivityFilter + ? "" + : `&sensitivity_tier=not.in.(${excluded.join(",")})`; + + if (noSensitivityFilter) { + console.warn( + "[weekly-digest] WARNING: --no-sensitivity-filter is set. " + + "ALL thoughts (including any that would be tagged restricted/personal) " + + "will be sent to the LLM and delivery target. You have accepted the " + + "data-leakage risk. Do NOT use this flag on a brain that contains " + + "secrets, health data, or other material you don't want exfiltrated.", + ); + } const headers = { apikey: cfg.serviceKey, @@ -199,34 +226,45 @@ async function fetchThoughts(cfg, { windowDays, includePersonal }) { }; const all = []; - let useFilter = true; for (let offset = 0; offset < FETCH_HARD_CAP; offset += PAGE_SIZE) { const limit = Math.min(PAGE_SIZE, FETCH_HARD_CAP - offset); const url = `${cfg.baseUrl}/rest/v1/thoughts` + - `?select=id,content,created_at,importance,metadata,sensitivity_tier` + + `?select=${selectCols}` + `&created_at=gte.${sinceIso}` + `&order=created_at.desc` + `&limit=${limit}&offset=${offset}` + - (useFilter ? excludeFilter : ""); + excludeFilter; const res = await fetch(url, { headers }); // If sensitivity_tier column doesn't exist on this install, PostgREST - // returns 400 with "column does not exist". Retry once without filter - // and warn — the user may not have the sensitivity-tiers primitive yet. - if (!res.ok && useFilter && res.status === 400) { + // returns 400 with "column does not exist". FAIL CLOSED: do not retry + // unfiltered, because that would silently leak restricted/personal + // thoughts on a brain that relies on the primitive's promise. Print a + // clear error and instruct the user how to proceed. + if (!res.ok && !noSensitivityFilter && res.status === 400) { const text = await res.text(); if (/sensitivity_tier/.test(text)) { - console.warn( - "[weekly-digest] sensitivity_tier column not found — " + - "falling back to unfiltered query. Install the sensitivity-tiers " + - "primitive to enable restricted/personal filtering.", + console.error( + "[weekly-digest] FATAL: sensitivity_tier column not found on public.thoughts.\n" + + "\n" + + "This recipe refuses to run unfiltered because the README promises\n" + + "that restricted thoughts never leave the database. Running without\n" + + "the column would silently send every row — including anything you\n" + + "would have tagged restricted/personal — to the LLM and delivery target.\n" + + "\n" + + "You have two options:\n" + + " 1. (Recommended) Install a sensitivity-tiers migration that adds the\n" + + " `sensitivity_tier TEXT` column to public.thoughts, then re-run.\n" + + " 2. Pass --no-sensitivity-filter to explicitly accept that ALL thoughts\n" + + " in the window will be exfiltrated. Do NOT do this on a brain that\n" + + " holds secrets, credentials, health data, or private correspondence.\n" + + "\n" + + "PostgREST error detail: " + text, ); - useFilter = false; - offset -= PAGE_SIZE; // retry this page without the filter - continue; + process.exit(1); } throw new Error(`thoughts fetch failed: ${res.status} ${text}`); } @@ -443,6 +481,7 @@ async function main() { const pool = await fetchThoughts(cfg, { windowDays: args.window, includePersonal: args.includePersonal, + noSensitivityFilter: args.noSensitivityFilter, }); console.log(`[weekly-digest] fetched ${pool.length} thoughts from window`); From 849ff766f1df192c399bcc57d7a19aa845c044e8 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:23:31 -0400 Subject: [PATCH 007/125] [recipes] Fix WR-02: SUPABASE_* canonical env vars with OPEN_BRAIN_* fallback --- recipes/lint-sweep/README.md | 16 +++++++---- recipes/lint-sweep/lint-sweep.mjs | 48 +++++++++++++++++++++++++------ recipes/lint-sweep/metadata.json | 3 +- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index 0fa396dd7..d01670951 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -37,8 +37,9 @@ LINT SWEEP -- CREDENTIAL TRACKER -------------------------------------- FROM YOUR OPEN BRAIN SETUP - Supabase project URL: ____________ (OPEN_BRAIN_URL) - Supabase service role key: ____________ (OPEN_BRAIN_SERVICE_KEY) + Supabase project URL: ____________ (SUPABASE_URL) + Supabase service role key: ____________ (SUPABASE_SERVICE_ROLE_KEY) + (Legacy OPEN_BRAIN_URL / OPEN_BRAIN_SERVICE_KEY are accepted as fallbacks.) OPTIONAL (TIER 3 ONLY) OpenRouter API key: ____________ (OPENROUTER_API_KEY) @@ -59,13 +60,16 @@ OPTIONAL (TIER 3 ONLY) ```bash cat > .env.local <<'EOF' - OPEN_BRAIN_URL=https://your-project.supabase.co - OPEN_BRAIN_SERVICE_KEY=your-service-role-key + SUPABASE_URL=https://your-project.supabase.co + SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # Optional: OPENROUTER_API_KEY=sk-or-v1-... EOF chmod 600 .env.local ``` + > [!NOTE] + > This recipe now uses the standard `SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` variable names used by every other OB1 recipe, so a shared `.env.local` works across the whole repo. The legacy `OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` names are still accepted (with a deprecation warning) for backward compatibility. + > [!WARNING] > The service role key bypasses Row Level Security. Keep `.env.local` out of version control and restrict its file permissions. @@ -240,8 +244,8 @@ After each run, open the latest `lint-report-YYYY-MM-DD.md`, triage the findings ## Troubleshooting -**Issue: `ERROR: OPEN_BRAIN_URL and OPEN_BRAIN_SERVICE_KEY must be set`** -Solution: Create `.env.local` in the same directory as `lint-sweep.mjs` with both variables, or export them in your shell. Fall-through order is `process.env` → `.env.local` → `.env`. +**Issue: `ERROR: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set`** +Solution: Create `.env.local` in the same directory as `lint-sweep.mjs` with both variables, or export them in your shell. Fall-through order is `process.env` → `.env.local` → `.env`. The legacy `OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` names are still accepted (with a one-line deprecation warning). **Issue: Tier 1 shows `content_fingerprint column missing — see recipes/content-fingerprint-dedup`** Solution: Your brain predates the [content-fingerprint-dedup](../content-fingerprint-dedup/) primitive. Apply that recipe (and the [fingerprint-dedup-backfill](../fingerprint-dedup-backfill/) recipe) to get duplicate detection. diff --git a/recipes/lint-sweep/lint-sweep.mjs b/recipes/lint-sweep/lint-sweep.mjs index fe1d09887..a989feee9 100644 --- a/recipes/lint-sweep/lint-sweep.mjs +++ b/recipes/lint-sweep/lint-sweep.mjs @@ -28,9 +28,13 @@ * * Environment (loaded from .env or .env.local in the script directory or * from process.env): - * OPEN_BRAIN_URL — Supabase project URL (e.g., https://xyz.supabase.co) - * OPEN_BRAIN_SERVICE_KEY — Supabase service role key - * OPENROUTER_API_KEY — OpenRouter key (Tier 3 only; omit to skip) + * SUPABASE_URL — Supabase project URL (e.g., https://xyz.supabase.co) + * SUPABASE_SERVICE_ROLE_KEY — Supabase service role key + * OPENROUTER_API_KEY — OpenRouter key (Tier 3 only; omit to skip) + * + * Legacy aliases (deprecated, accepted with a warning): + * OPEN_BRAIN_URL → use SUPABASE_URL + * OPEN_BRAIN_SERVICE_KEY → use SUPABASE_SERVICE_ROLE_KEY * * Exit codes: * 0 — report generated successfully @@ -69,6 +73,25 @@ function envVar(name) { return process.env[name] || fileEnv[name] || ""; } +// Resolve a variable that supports a legacy alias. Prefer `primary`; fall back +// to `legacy`. If the legacy name is what actually resolved the value, log a +// one-line deprecation warning (once per key) so consumers migrate to the +// canonical SUPABASE_* names shared by every other OB1 recipe. +const _deprecationWarned = new Set(); +function envVarWithLegacy(primary, legacy) { + const fromPrimary = envVar(primary); + if (fromPrimary) return fromPrimary; + const fromLegacy = envVar(legacy); + if (fromLegacy && !_deprecationWarned.has(legacy)) { + console.warn( + `[lint-sweep] WARNING: ${legacy} is deprecated; prefer ${primary} ` + + `(matches every other OB1 recipe and shared .env.local setups).` + ); + _deprecationWarned.add(legacy); + } + return fromLegacy; +} + // ── args ──────────────────────────────────────────────────────────────────── function parseArgs(argv) { @@ -147,9 +170,13 @@ Options: --help, -h Show this help Env (from .env, .env.local, or process.env): - OPEN_BRAIN_URL Supabase project URL - OPEN_BRAIN_SERVICE_KEY Supabase service role key - OPENROUTER_API_KEY OpenRouter key (Tier 3 only) + SUPABASE_URL Supabase project URL + SUPABASE_SERVICE_ROLE_KEY Supabase service role key + OPENROUTER_API_KEY OpenRouter key (Tier 3 only) + +Legacy aliases (deprecated, accepted with warning): + OPEN_BRAIN_URL → SUPABASE_URL + OPEN_BRAIN_SERVICE_KEY → SUPABASE_SERVICE_ROLE_KEY `.trim(); // ── Supabase REST helpers ─────────────────────────────────────────────────── @@ -659,10 +686,13 @@ function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { async function main() { const args = parseArgs(process.argv.slice(2)); - const baseUrl = envVar("OPEN_BRAIN_URL"); - const serviceKey = envVar("OPEN_BRAIN_SERVICE_KEY"); + const baseUrl = envVarWithLegacy("SUPABASE_URL", "OPEN_BRAIN_URL"); + const serviceKey = envVarWithLegacy("SUPABASE_SERVICE_ROLE_KEY", "OPEN_BRAIN_SERVICE_KEY"); if (!baseUrl || !serviceKey) { - console.error("ERROR: OPEN_BRAIN_URL and OPEN_BRAIN_SERVICE_KEY must be set (env or .env.local)."); + console.error( + "ERROR: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set (env or .env.local). " + + "Legacy OPEN_BRAIN_URL / OPEN_BRAIN_SERVICE_KEY are accepted as fallbacks with a deprecation warning." + ); process.exit(1); } const db = makeRestClient(baseUrl, serviceKey); diff --git a/recipes/lint-sweep/metadata.json b/recipes/lint-sweep/metadata.json index 16ac5851b..9258cbac8 100644 --- a/recipes/lint-sweep/metadata.json +++ b/recipes/lint-sweep/metadata.json @@ -10,7 +10,8 @@ "requires": { "open_brain": true, "services": ["OpenRouter (Tier 3 only)"], - "tools": ["Node.js 18+"] + "tools": ["Node.js 18+"], + "env": ["SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY"] }, "tags": ["quality", "audit", "maintenance", "contradictions", "orphans", "staleness"], "difficulty": "intermediate", From c6cabbad3c8629ae10d631b5b9a3b5aaa4777b78 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:23:36 -0400 Subject: [PATCH 008/125] [recipes] Fix REVIEW-CODEX-P1-1: read importance from metadata for stock OB1 compat --- recipes/weekly-digest/README.md | 2 +- recipes/weekly-digest/weekly-digest.mjs | 49 ++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md index cffab8088..27b37eba6 100644 --- a/recipes/weekly-digest/README.md +++ b/recipes/weekly-digest/README.md @@ -13,7 +13,7 @@ This is a "consumption format" companion to your capture habit. Captures alone p 1. **Query** `public.thoughts` for the last `--window` days (default 7) via PostgREST. 2. **Filter** by `sensitivity_tier` — restricted is always excluded; personal is excluded by default (opt in with `--include-personal`). 3. **Paginate** the full window so a busy capture day can't push earlier high-signal thoughts out of the sample. Capped at 400 thoughts per run. -4. **Rank** by `importance` (highest first, recency as tiebreaker). If fewer than 10 thoughts clear the `--min-importance` threshold, the pool widens to the top 60 by importance + recency so a quiet week still produces something worth reading. +4. **Rank** by importance (highest first, recency as tiebreaker). Importance is read from `metadata.importance` on each thought — stock OB1 `public.thoughts` has no top-level `importance` column, so capture pipelines that score importance should stash the value under `metadata.importance`. Thoughts without a score fall to `0`. If fewer than 10 thoughts clear the `--min-importance` threshold, the script logs a widening notice and falls back to the top 60 by importance + recency so a quiet week still produces something worth reading. (On a brain that doesn't score importance, pass `--min-importance=0`.) 5. **Synthesize** via Claude (Anthropic API direct, or OpenRouter as fallback). The system prompt asks for a Telegram-formatted digest under 1500 characters with fixed sections. 6. **Deliver** to Telegram (default), stdout, or a local markdown file under `./digests/YYYY-MM-DD.md`. diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 4ecd444cd..c09e46f54 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -203,9 +203,17 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi const excluded = includePersonal ? ["restricted"] : ["restricted", "personal"]; // When --no-sensitivity-filter is set, skip the filter entirely so a missing // column won't trip the 400. Otherwise, apply the exclusion filter. + // + // NOTE on importance: stock OB1 `public.thoughts` does NOT have an + // `importance` column. This recipe reads it from `metadata->>'importance'` + // on the client side (see `thoughtImportance()` below), so we do not select + // a bare `importance` column here. Installs that do add a native + // `importance` column can expose it via `metadata.importance` in their + // capture pipeline, or future work can add a COALESCE path that prefers + // the native column when present. const selectCols = noSensitivityFilter - ? "id,content,created_at,importance,metadata" - : "id,content,created_at,importance,metadata,sensitivity_tier"; + ? "id,content,created_at,metadata" + : "id,content,created_at,metadata,sensitivity_tier"; const excludeFilter = noSensitivityFilter ? "" : `&sensitivity_tier=not.in.(${excluded.join(",")})`; @@ -282,6 +290,27 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi return all; } +/** + * Read the importance score for a thought. Stock OB1 has no `importance` + * column on public.thoughts; capture pipelines that score importance stash + * it under `metadata.importance`. We prefer a native top-level `importance` + * if a given install happens to have one (via COALESCE-style logic), then + * fall back to `metadata.importance`, then 0. + * + * Accepts number or numeric string in metadata (JSON round-trip safety). + */ +function thoughtImportance(t) { + const native = t?.importance; + if (typeof native === "number" && Number.isFinite(native)) return native; + const m = t?.metadata?.importance; + if (typeof m === "number" && Number.isFinite(m)) return m; + if (typeof m === "string") { + const n = Number(m); + if (Number.isFinite(n)) return n; + } + return 0; +} + /** * Ranks the fetched pool by importance, falling back to recency when * importance ties or is missing. If there aren't enough thoughts at or above @@ -291,13 +320,21 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi */ function rankAndTrim(thoughts, minImportance) { const sorted = [...thoughts].sort((a, b) => { - const ai = a.importance ?? 0; - const bi = b.importance ?? 0; + const ai = thoughtImportance(a); + const bi = thoughtImportance(b); if (bi !== ai) return bi - ai; return String(b.created_at).localeCompare(String(a.created_at)); }); - const highImportance = sorted.filter((t) => (t.importance ?? 0) >= minImportance); + const highImportance = sorted.filter((t) => thoughtImportance(t) >= minImportance); + if (highImportance.length < 10) { + console.warn( + `[weekly-digest] only ${highImportance.length} thought(s) at or above ` + + `--min-importance=${minImportance}; widening pool to top 60 by importance+recency. ` + + `If your brain doesn't score importance (stock OB1 has no importance column ` + + `and this recipe reads metadata.importance), pass --min-importance=0.`, + ); + } const pool = highImportance.length >= 10 ? highImportance : sorted.slice(0, 60); return pool.slice(0, 200); } @@ -319,7 +356,7 @@ function buildUserPrompt(thoughts, startDate, endDate) { id: t.id, date: String(t.created_at || "").slice(0, 10), type: t.metadata?.type ?? null, - importance: t.importance ?? null, + importance: thoughtImportance(t) || null, content: String(t.content || "").slice(0, 280), topics: (t.metadata?.topics ?? []).slice(0, 5), tags: (t.metadata?.tags ?? []).slice(0, 5), From 9938c2ab1154b63bc1524f94d17953e00fa75911 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:24:27 -0400 Subject: [PATCH 009/125] [recipes] Fix REVIEW-CODEX-P2: --dry-run skips Telegram creds and implies stdout --- recipes/weekly-digest/weekly-digest.mjs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index c09e46f54..338f16153 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -17,7 +17,7 @@ * node weekly-digest.mjs --include-personal # include sensitivity_tier=personal * node weekly-digest.mjs --model=claude-haiku-4-5-20251001 * node weekly-digest.mjs --min-importance=3 # lower threshold - * node weekly-digest.mjs --dry-run # synthesize + print, deliver nothing + * node weekly-digest.mjs --dry-run # synthesize + print, no delivery (implies --output=stdout) * * Env vars: * OPEN_BRAIN_URL Your Supabase project URL (required) @@ -109,6 +109,13 @@ function parseArgs(argv) { throw new Error(`Unknown flag: ${raw}`); } } + // --dry-run implies --output=stdout unless the user explicitly chose + // another output. This keeps the smoke-test path friction-free: you can + // pass just `--dry-run` without also having to remember `--output=stdout` + // to avoid the Telegram credential check. + if (args.dryRun && !args.outputExplicit) { + args.output = "stdout"; + } return args; } @@ -156,13 +163,18 @@ function loadConfig(args) { let telegramBotToken = null; let telegramChatId = null; - if (args.output === "telegram") { + // Only require Telegram credentials when we will actually send to Telegram. + // A --dry-run never ships anywhere, even if --output=telegram is passed, + // so it shouldn't demand tokens the user hasn't configured. + const willDeliverTelegram = args.output === "telegram" && !args.dryRun; + if (willDeliverTelegram) { telegramBotToken = process.env.TELEGRAM_BOT_TOKEN; telegramChatId = process.env.TELEGRAM_CHAT_ID; if (!telegramBotToken || !telegramChatId) { throw new Error( "--output=telegram requires TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID env vars. " + - "Use --output=stdout or --output=file if you don't have Telegram configured.", + "Use --output=stdout or --output=file if you don't have Telegram configured, " + + "or pass --dry-run to synthesize without delivery.", ); } } From defd8b2c2c4d5ad1c94308ffaf3dc9ab4426efe6 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:24:55 -0400 Subject: [PATCH 010/125] [recipes] Fix WR-01: rename lint-sweep.mjs to .js with type:module for CI whitelist --- recipes/lint-sweep/README.md | 18 ++++++------- .../{lint-sweep.mjs => lint-sweep.js} | 25 +++++++++++-------- recipes/lint-sweep/package.json | 18 +++++++++++++ 3 files changed, 42 insertions(+), 19 deletions(-) rename recipes/lint-sweep/{lint-sweep.mjs => lint-sweep.js} (97%) create mode 100644 recipes/lint-sweep/package.json diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index d01670951..15fbd39cb 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -80,7 +80,7 @@ OPTIONAL (TIER 3 ONLY) 4. Verify the script runs: ```bash - node lint-sweep.mjs --tier=1 + node lint-sweep.js --tier=1 ``` You should see progress output and a `lint-report-YYYY-MM-DD.md` file in your working directory. @@ -90,21 +90,21 @@ OPTIONAL (TIER 3 ONLY) Run every tier against the defaults: ```bash -node lint-sweep.mjs +node lint-sweep.js ``` Pick a single tier: ```bash -node lint-sweep.mjs --tier=1 # SQL-only, free -node lint-sweep.mjs --tier=2 # graph-based, free -node lint-sweep.mjs --tier=3 --max-llm-calls=5 # LLM, capped +node lint-sweep.js --tier=1 # SQL-only, free +node lint-sweep.js --tier=2 # graph-based, free +node lint-sweep.js --tier=3 --max-llm-calls=5 # LLM, capped ``` Custom output path and sample size: ```bash -node lint-sweep.mjs \ +node lint-sweep.js \ --tier=all \ --sample-size=200 \ --max-llm-calls=10 \ @@ -185,7 +185,7 @@ finished_at: 2026-04-17T14:22:11.031Z --- -**Safety:** `lint-sweep.mjs` is read-only. Every finding above is a suggestion +**Safety:** `lint-sweep.js` is read-only. Every finding above is a suggestion for a human to review. Before acting on any item, verify the thought with `get_thought` or the web UI. Never delete or edit a thought based solely on this report. @@ -232,7 +232,7 @@ Run the sweep weekly with cron (Linux/macOS): ```cron # Every Sunday at 02:00 local — writes ~/lint-reports/lint-report-YYYY-MM-DD.md -0 2 * * 0 cd /home/you/lint-sweep && /usr/bin/node lint-sweep.mjs \ +0 2 * * 0 cd /home/you/lint-sweep && /usr/bin/node lint-sweep.js \ --tier=all --max-llm-calls=5 \ --report=/home/you/lint-reports/lint-report-$(date +\%F).md \ >> /home/you/lint-reports/lint-sweep.log 2>&1 @@ -245,7 +245,7 @@ After each run, open the latest `lint-report-YYYY-MM-DD.md`, triage the findings ## Troubleshooting **Issue: `ERROR: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set`** -Solution: Create `.env.local` in the same directory as `lint-sweep.mjs` with both variables, or export them in your shell. Fall-through order is `process.env` → `.env.local` → `.env`. The legacy `OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` names are still accepted (with a one-line deprecation warning). +Solution: Create `.env.local` in the same directory as `lint-sweep.js` with both variables, or export them in your shell. Fall-through order is `process.env` → `.env.local` → `.env`. The legacy `OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` names are still accepted (with a one-line deprecation warning). **Issue: Tier 1 shows `content_fingerprint column missing — see recipes/content-fingerprint-dedup`** Solution: Your brain predates the [content-fingerprint-dedup](../content-fingerprint-dedup/) primitive. Apply that recipe (and the [fingerprint-dedup-backfill](../fingerprint-dedup-backfill/) recipe) to get duplicate detection. diff --git a/recipes/lint-sweep/lint-sweep.mjs b/recipes/lint-sweep/lint-sweep.js similarity index 97% rename from recipes/lint-sweep/lint-sweep.mjs rename to recipes/lint-sweep/lint-sweep.js index a989feee9..ee89e30d6 100644 --- a/recipes/lint-sweep/lint-sweep.mjs +++ b/recipes/lint-sweep/lint-sweep.js @@ -1,6 +1,11 @@ #!/usr/bin/env node /** - * lint-sweep.mjs — Bounded weekly brain-quality audit for Open Brain. + * lint-sweep.js — Bounded weekly brain-quality audit for Open Brain. + * + * ESM module; see the sibling package.json for `"type": "module"`. We use a + * `.js` extension (rather than `.mjs`) to match the OB1 Rule 6 artifact + * whitelist in `.github/workflows/ob1-gate.yml`, which only admits + * `.sql|.ts|.js|.py`. * * Inspired by Karpathy's "lint" concept and the CRATE CLI. Scans the * `public.thoughts` table for quality issues across three cost tiers: @@ -19,12 +24,12 @@ * gates any destructive action — this script only reports. * * Usage: - * node lint-sweep.mjs # all three tiers, default caps - * node lint-sweep.mjs --tier=1 # SQL-only sweep - * node lint-sweep.mjs --tier=2 # graph-based sweep - * node lint-sweep.mjs --tier=3 --max-llm-calls=10 # LLM contradiction sampling - * node lint-sweep.mjs --tier=all --sample-size=200 - * node lint-sweep.mjs --report=./out/weekly.md + * node lint-sweep.js # all three tiers, default caps + * node lint-sweep.js --tier=1 # SQL-only sweep + * node lint-sweep.js --tier=2 # graph-based sweep + * node lint-sweep.js --tier=3 --max-llm-calls=10 # LLM contradiction sampling + * node lint-sweep.js --tier=all --sample-size=200 + * node lint-sweep.js --report=./out/weekly.md * * Environment (loaded from .env or .env.local in the script directory or * from process.env): @@ -153,9 +158,9 @@ function parseArgs(argv) { } const HELP = ` -lint-sweep.mjs — bounded brain-quality audit for Open Brain +lint-sweep.js — bounded brain-quality audit for Open Brain -Usage: node lint-sweep.mjs [options] +Usage: node lint-sweep.js [options] Options: --tier=<1|2|3|all> Which tier(s) to run (default: all) @@ -675,7 +680,7 @@ function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { lines.push("---"); lines.push(""); - lines.push("**Safety:** `lint-sweep.mjs` is read-only. Every finding above is a suggestion for a human to review. "); + lines.push("**Safety:** `lint-sweep.js` is read-only. Every finding above is a suggestion for a human to review. "); lines.push("Before acting on any item, verify the thought with `get_thought` or the web UI. "); lines.push("Never delete or edit a thought based solely on this report."); return lines.join("\n"); diff --git a/recipes/lint-sweep/package.json b/recipes/lint-sweep/package.json new file mode 100644 index 000000000..65bfc4894 --- /dev/null +++ b/recipes/lint-sweep/package.json @@ -0,0 +1,18 @@ +{ + "name": "ob1-lint-sweep", + "version": "1.0.0", + "description": "Open Brain Lint Sweep — bounded brain-quality audit (read-only).", + "private": true, + "type": "module", + "main": "lint-sweep.js", + "bin": { + "ob1-lint-sweep": "./lint-sweep.js" + }, + "engines": { + "node": ">=18" + }, + "scripts": { + "lint-sweep": "node lint-sweep.js" + }, + "license": "FSL-1.1-MIT" +} From 8e812b5e95b0b6dcb042d222ee5a1ae2ee46f176 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:25:13 -0400 Subject: [PATCH 011/125] [recipes] Fix REVIEW-CODEX-P3: distinguish source_thought_count_used vs pool_size --- recipes/weekly-digest/weekly-digest.mjs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 338f16153..5df42ed29 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -492,12 +492,19 @@ async function deliverTelegram(cfg, text) { return messageIds; } -function deliverFile(text, startDate, endDate, model, sourceCount) { +function deliverFile(text, startDate, endDate, model, counts) { const dir = path.resolve(process.cwd(), "digests"); fs.mkdirSync(dir, { recursive: true }); const filename = `${endDate.toISOString().slice(0, 10)}.md`; const filepath = path.join(dir, filename); + // We record two distinct counts so the saved metadata is honest about + // what influenced the digest: + // - source_thought_count_used: how many rows were actually serialized + // into the LLM prompt (capped at SYNTHESIZE_INPUT_CAP). + // - source_pool_size: the full ranked pool before the prompt cap. + // If pool_size > count_used, the LLM only saw the top `count_used` by + // importance+recency; the rest were ranked but truncated before send. const frontmatter = [ "---", `title: Weekly Digest ${endDate.toISOString().slice(0, 10)}`, @@ -506,7 +513,8 @@ function deliverFile(text, startDate, endDate, model, sourceCount) { `period_end: ${endDate.toISOString().slice(0, 10)}`, `generated_at: ${new Date().toISOString()}`, `generated_by_model: ${model}`, - `source_thought_count: ${sourceCount}`, + `source_thought_count_used: ${counts.used}`, + `source_pool_size: ${counts.poolSize}`, "tags: [weekly-digest, synthesis]", "---", "", @@ -564,12 +572,17 @@ async function main() { } if (args.output === "file") { + // `used` reflects only what was serialized into the prompt (top + // SYNTHESIZE_INPUT_CAP by importance+recency); `poolSize` is the full + // ranked pool before the cap. Once the pool exceeds the cap, these + // differ and both are recorded for auditability. + const used = Math.min(ranked.length, SYNTHESIZE_INPUT_CAP); const filepath = deliverFile( digest, startDate, endDate, args.model, - ranked.length, + { used, poolSize: ranked.length }, ); console.log(`[weekly-digest] wrote ${filepath}`); return; From 8acf9d31e92b42ebf404019912ea5b2c87c2360d Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:25:58 -0400 Subject: [PATCH 012/125] [recipes] Fix WR-03: document Tier 1/2 sampling caps in report --- recipes/lint-sweep/lint-sweep.js | 45 +++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index ee89e30d6..ab7be02d8 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -567,22 +567,47 @@ function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { lines.push("*Read-only audit. This script never mutates thoughts.*"); lines.push(""); + // Scan scope — make sampling caps explicit so the counts below are not + // misread as whole-brain totals on large installs. + lines.push("## Scan scope"); + lines.push(""); + lines.push("This run inspects bounded samples, not your entire brain. Counts below are relative to these samples."); + lines.push(""); + if (tier1) { + lines.push("- **Tier 1** — most recent **2000 thoughts** (ordered by `id desc`) for orphan/over-tag/length checks; up to **5000 rows** with a populated `content_fingerprint` for duplicate detection; full-table exact row counts for `thoughts` and `content_fingerprint IS NULL` (no cap)."); + } + if (tier2) { + lines.push("- **Tier 2** — first **500 high-importance thoughts** (`importance >= 4`), first **2000 entities**, first **5000 edges**."); + } + if (tier3) { + if (tier3.enabled) { + lines.push(`- **Tier 3** — up to **${args.sampleSize} thoughts** from the last **${args.days} days**, batched ~20 per LLM call, hard-capped at **${args.maxLlmCalls} LLM calls**.`); + } else { + lines.push(`- **Tier 3** — skipped (${tier3.skippedReason}).`); + } + } + lines.push(""); + lines.push("On brains larger than these caps, Tier 1/2 counts represent a **slice**, not the global total. Example: \"Entities with zero edges: 12\" under a 2000-entity cap means *12 isolated entities among the first 2000 returned*, not \"12 total isolated entities.\" For whole-brain coverage, run the SQL views in [`views.sql`](./views.sql) directly."); + lines.push(""); + // Summary header const tier3Count = tier3 ? Object.values(tier3.findings || {}).reduce((n, a) => n + (Array.isArray(a) ? a.length : 0), 0) : 0; lines.push("## Summary"); lines.push(""); + lines.push("*Counts below reflect the bounded scan scope described above — not whole-brain totals.*"); + lines.push(""); if (tier1) { - lines.push(`- Total thoughts inspected (Tier 1 sample): ${tier1.totalThoughts}`); - lines.push(`- Orphans by tag (recent 2000): ${tier1.orphansByTag}`); - lines.push(`- Exact-duplicate fingerprint groups: ${Array.isArray(tier1.exactDuplicates) ? tier1.exactDuplicates.filter((x) => x.ids).length : 0}`); - lines.push(`- Rows missing content_fingerprint: ${tier1.noFingerprint}`); - lines.push(`- Low-signal noise candidates: ${tier1.lowSignalNoise}`); + lines.push(`- Total thoughts in table (exact count, uncapped): ${tier1.totalThoughts}`); + lines.push(`- Orphans by tag (in recent 2000 sampled): ${tier1.orphansByTag}`); + lines.push(`- Exact-duplicate fingerprint groups (in first 5000 fingerprinted rows): ${Array.isArray(tier1.exactDuplicates) ? tier1.exactDuplicates.filter((x) => x.ids).length : 0}`); + lines.push(`- Rows missing content_fingerprint (exact count, uncapped): ${tier1.noFingerprint}`); + lines.push(`- Low-signal noise candidates (in recent 2000 sampled): ${tier1.lowSignalNoise}`); } if (tier2) { - lines.push(`- High-importance isolated (no entity links): ${tier2.highImportanceIsolated.length}`); - lines.push(`- Entities with zero edges: ${tier2.entitiesWithNoEdges}`); + lines.push(`- High-importance isolated — no entity links (in first 500 high-importance sampled): ${tier2.highImportanceIsolated.length}`); + lines.push(`- Entities with zero edges (among first 2000 entities ∩ first 5000 edges): ${tier2.entitiesWithNoEdges}`); } if (tier3) { if (tier3.enabled) { @@ -623,18 +648,20 @@ function renderReport({ args, tier1, tier2, tier3, startedAt, finishedAt }) { if (tier2) { lines.push("## Tier 2 — Graph-based lint (free)"); lines.push(""); + lines.push("*Scope: first 500 high-importance thoughts, first 2000 entities, first 5000 edges. Counts below are within that slice, not the whole brain.*"); + lines.push(""); if (tier2.graphTablesMissing.length > 0) { lines.push(`*Graph tables absent: ${tier2.graphTablesMissing.join(", ")}. Tier 2 requires the \`entity-extraction\` schema (which ships \`entities\`, \`edges\`, \`thought_entities\`) — see PRs #197 and #199. The \`ob-graph\` recipe uses different table names and does NOT satisfy this dependency.*`); lines.push(""); } - lines.push(`- High-importance (≥4) thoughts with no entity links: **${tier2.highImportanceIsolated.length}**`); + lines.push(`- High-importance (≥4) thoughts with no entity links (in first 500 high-importance sampled): **${tier2.highImportanceIsolated.length}**`); for (const row of tier2.highImportanceIsolated.slice(0, 15)) { lines.push(` - #${row.id} (imp=${row.importance}, ${row.created_at?.slice(0, 10)}) — ${row.preview}${row.preview.length >= 120 ? "…" : ""}`); } if (tier2.highImportanceIsolated.length > 15) { lines.push(` - …and ${tier2.highImportanceIsolated.length - 15} more`); } - lines.push(`- Entities with zero edges: **${tier2.entitiesWithNoEdges}**`); + lines.push(`- Entities with zero edges (among first 2000 entities ∩ first 5000 edges): **${tier2.entitiesWithNoEdges}**`); lines.push(""); } From 126037e4e766e0796757bff1dca06ff006d6230b Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:26:28 -0400 Subject: [PATCH 013/125] [recipes] Fix REVIEW-CODEX-P3: remove dead rpc() and dangling counters --- recipes/lint-sweep/lint-sweep.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index ab7be02d8..949de45be 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -226,22 +226,7 @@ function makeRestClient(baseUrl, serviceKey) { return Array.isArray(arr) ? arr.length : 0; } - async function rpc(fnName, payload) { - const url = `${rest}/rpc/${fnName}`; - const res = await fetch(url, { - method: "POST", - headers, - body: JSON.stringify(payload || {}), - }); - if (!res.ok) { - const body = await res.text().catch(() => ""); - throw new Error(`RPC ${fnName} → ${res.status} ${body.slice(0, 300)}`); - } - const text = await res.text(); - return text ? JSON.parse(text) : null; - } - - return { get, count, rpc }; + return { get, count }; } // ── Tier 1: SQL-only lint (free) ──────────────────────────────────────────── @@ -338,8 +323,6 @@ async function tier2GraphLint(db, args) { const out = { highImportanceIsolated: [], // importance >= 4 thoughts with no entity links entitiesWithNoEdges: 0, - thoughtEntityDanglingThought: 0, // thought_entities rows whose thought id is gone - thoughtEntityDanglingEntity: 0, // thought_entities rows whose entity id is gone graphTablesMissing: [], // which of (entities, edges, thought_entities) are absent }; From 237534021ec6ea85ee0b84906bb0d11ff707fa12 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:26:41 -0400 Subject: [PATCH 014/125] [recipes] Fix WR-01: SUPABASE_* canonical env vars --- recipes/weekly-digest/README.md | 22 ++++++----- recipes/weekly-digest/metadata.json | 11 +++++- recipes/weekly-digest/weekly-digest.mjs | 52 +++++++++++++++++++------ 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md index 27b37eba6..b2b86553d 100644 --- a/recipes/weekly-digest/README.md +++ b/recipes/weekly-digest/README.md @@ -35,9 +35,9 @@ Copy this block into a text editor and fill it in as you go. WEEKLY DIGEST -- CREDENTIAL TRACKER -------------------------------------- -FROM YOUR OPEN BRAIN SETUP - Open Brain URL: ____________ (your-project-ref.supabase.co) - Open Brain service key: ____________ +FROM YOUR OPEN BRAIN (SUPABASE) SETUP + SUPABASE_URL: ____________ (your-project-ref.supabase.co) + SUPABASE_SERVICE_ROLE_KEY: ____________ LLM (pick one) Anthropic API key: ____________ @@ -56,13 +56,15 @@ TELEGRAM DELIVERY (optional) 2. Export the required env vars for your shell, a `.env.local` file, or your scheduler's secret store. The script does not load `.env` files directly — keep it simple and let your runner (cron, systemd, GitHub Actions, `dotenv-cli`) handle that. ```bash -export OPEN_BRAIN_URL="https://your-project-ref.supabase.co" -export OPEN_BRAIN_SERVICE_KEY="your-service-role-key" +export SUPABASE_URL="https://your-project-ref.supabase.co" +export SUPABASE_SERVICE_ROLE_KEY="your-service-role-key" export ANTHROPIC_API_KEY="sk-ant-..." # or OPENROUTER_API_KEY export TELEGRAM_BOT_TOKEN="123456:..." # optional export TELEGRAM_CHAT_ID="987654321" # optional ``` +> `SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` match the rest of the `recipes/` tree so you can share a single `.env.local` across recipes. `OPEN_BRAIN_URL` and `OPEN_BRAIN_SERVICE_KEY` are accepted as legacy aliases for back-compat but emit a one-time deprecation warning. + 3. Smoke test it with `--dry-run` first so nothing ships anywhere: ```bash @@ -176,8 +178,8 @@ jobs: node-version: "20" - name: Run digest env: - OPEN_BRAIN_URL: ${{ secrets.OPEN_BRAIN_URL }} - OPEN_BRAIN_SERVICE_KEY: ${{ secrets.OPEN_BRAIN_SERVICE_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} @@ -233,14 +235,14 @@ With `--output=telegram`, the digest arrives as one (or two, if long) messages i ## Troubleshooting -**Issue: `Missing OPEN_BRAIN_URL env var`** -Solution: Export `OPEN_BRAIN_URL` and `OPEN_BRAIN_SERVICE_KEY` before running. The script does not auto-load `.env` files — wrap the call in `dotenv-cli` (`npx dotenv -e .env.local -- node weekly-digest.mjs`) or let your scheduler inject the env. +**Issue: `Missing SUPABASE_URL env var`** +Solution: Export `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` before running. (`OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` also work as legacy aliases but print a deprecation warning.) The script does not auto-load `.env` files — wrap the call in `dotenv-cli` (`npx dotenv -e .env.local -- node weekly-digest.mjs`) or let your scheduler inject the env. **Issue: `sensitivity_tier column not found — falling back to unfiltered query`** Solution: Your Open Brain install doesn't have the sensitivity-tiers primitive yet. The script still works, but restricted/personal thoughts will be included in the pool. Install the primitive to enable filtering, or audit the output carefully if you're posting publicly. **Issue: `thoughts fetch failed: 401`** -Solution: `OPEN_BRAIN_SERVICE_KEY` is wrong or expired. This must be the **service_role** key (not the anon key), because the recipe needs to read rows regardless of RLS policies. Double-check in Supabase → Project Settings → API. +Solution: `SUPABASE_SERVICE_ROLE_KEY` is wrong or expired. This must be the **service_role** key (not the anon key), because the recipe needs to read rows regardless of RLS policies. Double-check in Supabase → Project Settings → API. **Issue: `Telegram sendMessage failed: 400 chat not found`** Solution: `TELEGRAM_CHAT_ID` is wrong, or you haven't messaged the bot yet. Send any message to your bot, then check `https://api.telegram.org/bot/getUpdates` — the numeric `chat.id` is what you want. diff --git a/recipes/weekly-digest/metadata.json b/recipes/weekly-digest/metadata.json index 0ad15e12e..30a1123ea 100644 --- a/recipes/weekly-digest/metadata.json +++ b/recipes/weekly-digest/metadata.json @@ -10,7 +10,16 @@ "requires": { "open_brain": true, "services": ["Telegram Bot API (optional)", "Anthropic API or OpenRouter"], - "tools": ["Node.js 18+"] + "tools": ["Node.js 18+"], + "env": { + "required": ["SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY"], + "llm_one_of": ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY"], + "optional": ["TELEGRAM_BOT_TOKEN", "TELEGRAM_CHAT_ID", "DIGEST_MODEL"], + "legacy_aliases": { + "OPEN_BRAIN_URL": "SUPABASE_URL", + "OPEN_BRAIN_SERVICE_KEY": "SUPABASE_SERVICE_ROLE_KEY" + } + } }, "tags": ["consumption-formats", "digest", "telegram", "synthesis", "scheduled"], "difficulty": "intermediate", diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 5df42ed29..45b1dd245 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -20,13 +20,15 @@ * node weekly-digest.mjs --dry-run # synthesize + print, no delivery (implies --output=stdout) * * Env vars: - * OPEN_BRAIN_URL Your Supabase project URL (required) - * OPEN_BRAIN_SERVICE_KEY Supabase service role key (required) - * ANTHROPIC_API_KEY Direct Anthropic key (preferred) - * OPENROUTER_API_KEY OpenRouter fallback (used if ANTHROPIC_API_KEY unset) - * TELEGRAM_BOT_TOKEN Required for --output=telegram - * TELEGRAM_CHAT_ID Required for --output=telegram - * DIGEST_MODEL Override default model (default: claude-opus-4-7) + * SUPABASE_URL Your Supabase project URL (required; canonical) + * SUPABASE_SERVICE_ROLE_KEY Supabase service role key (required; canonical) + * OPEN_BRAIN_URL Legacy alias for SUPABASE_URL (deprecated) + * OPEN_BRAIN_SERVICE_KEY Legacy alias for SUPABASE_SERVICE_ROLE_KEY (deprecated) + * ANTHROPIC_API_KEY Direct Anthropic key (preferred) + * OPENROUTER_API_KEY OpenRouter fallback (used if ANTHROPIC_API_KEY unset) + * TELEGRAM_BOT_TOKEN Required for --output=telegram + * TELEGRAM_CHAT_ID Required for --output=telegram + * DIGEST_MODEL Override default model (default: claude-opus-4-7) */ import fs from "node:fs"; @@ -145,10 +147,38 @@ function printHelp() { // ── Env validation ────────────────────────────────────────────────────────── function loadConfig(args) { - const openBrainUrl = process.env.OPEN_BRAIN_URL; - const openBrainKey = process.env.OPEN_BRAIN_SERVICE_KEY; - if (!openBrainUrl) throw new Error("Missing OPEN_BRAIN_URL env var"); - if (!openBrainKey) throw new Error("Missing OPEN_BRAIN_SERVICE_KEY env var"); + // Canonical env vars match the rest of the repo's recipes + // (SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY). OPEN_BRAIN_* is accepted as + // a legacy alias so existing setups keep working; a one-time deprecation + // warning nudges users toward the shared `.env.local` pattern. + const supabaseUrlPrimary = process.env.SUPABASE_URL; + const supabaseKeyPrimary = process.env.SUPABASE_SERVICE_ROLE_KEY; + const legacyUrl = process.env.OPEN_BRAIN_URL; + const legacyKey = process.env.OPEN_BRAIN_SERVICE_KEY; + const openBrainUrl = supabaseUrlPrimary || legacyUrl; + const openBrainKey = supabaseKeyPrimary || legacyKey; + if (!openBrainUrl) { + throw new Error( + "Missing SUPABASE_URL env var (legacy alias: OPEN_BRAIN_URL)", + ); + } + if (!openBrainKey) { + throw new Error( + "Missing SUPABASE_SERVICE_ROLE_KEY env var (legacy alias: OPEN_BRAIN_SERVICE_KEY)", + ); + } + if (!supabaseUrlPrimary && legacyUrl) { + console.warn( + "[weekly-digest] DEPRECATION: OPEN_BRAIN_URL is a legacy alias. " + + "Set SUPABASE_URL instead to share one .env.local across recipes.", + ); + } + if (!supabaseKeyPrimary && legacyKey) { + console.warn( + "[weekly-digest] DEPRECATION: OPEN_BRAIN_SERVICE_KEY is a legacy alias. " + + "Set SUPABASE_SERVICE_ROLE_KEY instead to share one .env.local across recipes.", + ); + } const anthropicKey = process.env.ANTHROPIC_API_KEY; const openrouterKey = process.env.OPENROUTER_API_KEY; From 5add28ddc073dfef845188bd3020bf445e7bf758 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:27:30 -0400 Subject: [PATCH 015/125] [recipes] Fix WR-02: replace dead primitive link with honest guidance --- recipes/weekly-digest/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md index b2b86553d..b35e07586 100644 --- a/recipes/weekly-digest/README.md +++ b/recipes/weekly-digest/README.md @@ -25,7 +25,7 @@ This is a "consumption format" companion to your capture habit. Captures alone p - `ANTHROPIC_API_KEY` (preferred — direct Anthropic API) - `OPENROUTER_API_KEY` (fallback — routes through OpenRouter) - **Optional:** a Telegram bot for delivery. If you don't have one, use `--output=stdout` or `--output=file` and skip the Telegram setup entirely. -- **Optional but recommended:** the [sensitivity-tiers primitive](../../primitives/) so `restricted` and `personal` filtering actually means something. If `sensitivity_tier` isn't present on your install, the script warns and falls back to an unfiltered query. +- **Required for the out-of-the-box safety guarantee:** a `sensitivity_tier TEXT` column on `public.thoughts`. No official Open Brain primitive ships this yet; if you haven't added the column, either install your own migration or wait for the sensitivity-tiers primitive to land upstream. On stock OB1, the recipe **fails closed** — it refuses to run and tells you how to proceed (see the Sensitivity section below). If you explicitly accept the data-leakage risk, pass `--no-sensitivity-filter` to run unfiltered. ## Credential Tracker @@ -120,16 +120,16 @@ If you already have a Telegram bot wired to Open Brain for capture (e.g., via a ## Sensitivity -Open Brain's [sensitivity-tiers](../../primitives/) primitive adds a `sensitivity_tier` column to `public.thoughts` with values like `standard`, `personal`, and `restricted`. The digest honors that signal: +This recipe expects a `sensitivity_tier TEXT` column on `public.thoughts` with values like `standard`, `personal`, and `restricted`. No official Open Brain primitive ships this today; once a `sensitivity-tiers` primitive lands upstream you can install it, or you can add the column via your own migration in the meantime. The digest honors the signal: - **`restricted`** — never included. These are thoughts you've explicitly flagged as off-limits; a synthesis pass that surfaces them to a Telegram chat defeats the purpose of the tier. - **`personal`** — excluded by default. Your week probably contains private material (health, relationships, finances) that you'd rather not summarize over an unencrypted wire to a third-party API. Pass `--include-personal` when you want the fuller picture — e.g., a private file output you keep locally. - **`standard`** (or null) — always included. -If the `sensitivity_tier` column isn't present on your install, the script logs a one-time warning and continues unfiltered. Install the sensitivity-tiers primitive to activate the filter. +**Fail-closed behavior on stock OB1:** if the `sensitivity_tier` column is not present (e.g., a vanilla Open Brain install), the recipe **refuses to run** and prints instructions. It does not silently fall back to an unfiltered query, because that would violate the guarantee below. To override this safety net — at your own risk — pass `--no-sensitivity-filter`; the script will print a loud warning and send every row in the window to the LLM and delivery target. > [!IMPORTANT] -> Filtering happens at the PostgREST query level, not after the fact. Restricted thoughts never leave the database, so they can never reach the LLM. +> Filtering happens at the PostgREST query level, not after the fact. When the `sensitivity_tier` column exists and `--no-sensitivity-filter` is NOT set, restricted thoughts never leave the database, so they can never reach the LLM. The only way restricted/personal rows can reach the LLM is if (a) the column is missing and you passed `--no-sensitivity-filter`, or (b) you misconfigured the column values. ## Scheduling @@ -238,8 +238,8 @@ With `--output=telegram`, the digest arrives as one (or two, if long) messages i **Issue: `Missing SUPABASE_URL env var`** Solution: Export `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` before running. (`OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` also work as legacy aliases but print a deprecation warning.) The script does not auto-load `.env` files — wrap the call in `dotenv-cli` (`npx dotenv -e .env.local -- node weekly-digest.mjs`) or let your scheduler inject the env. -**Issue: `sensitivity_tier column not found — falling back to unfiltered query`** -Solution: Your Open Brain install doesn't have the sensitivity-tiers primitive yet. The script still works, but restricted/personal thoughts will be included in the pool. Install the primitive to enable filtering, or audit the output carefully if you're posting publicly. +**Issue: `FATAL: sensitivity_tier column not found on public.thoughts`** +Solution: Your Open Brain install doesn't have a `sensitivity_tier` column on `public.thoughts`. The recipe fails closed rather than silently leak restricted thoughts to the LLM and Telegram. Options: (a) add the column via your own migration (or install the sensitivity-tiers primitive once it ships) and re-run; (b) if you understand the risk and your brain contains nothing sensitive, pass `--no-sensitivity-filter` to run unfiltered — the script will print a loud warning and proceed. **Issue: `thoughts fetch failed: 401`** Solution: `SUPABASE_SERVICE_ROLE_KEY` is wrong or expired. This must be the **service_role** key (not the anon key), because the recipe needs to read rows regardless of RLS policies. Double-check in Supabase → Project Settings → API. From 5a060f22beeed6e8877500e24f3a182780afbc8b Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:27:37 -0400 Subject: [PATCH 016/125] [recipes] Fix IN-04/IN-05: sanitize preview text, validate report path --- recipes/lint-sweep/lint-sweep.js | 49 +++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index 949de45be..609823bf9 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -154,6 +154,10 @@ function parseArgs(argv) { const date = new Date().toISOString().slice(0, 10); args.report = path.join(process.cwd(), `lint-report-${date}.md`); } + // Refuse report paths that escape cwd — stops typos like `--report=../x` + // from silently overwriting unrelated files. The default above is already + // under cwd, so only user-supplied values can fail this check. + args.report = resolveReportPath(args.report); return args; } @@ -229,6 +233,49 @@ function makeRestClient(baseUrl, serviceKey) { return { get, count }; } +// ── helpers ───────────────────────────────────────────────────────────────── + +/** + * Make a thought-content snippet safe to embed inside a markdown report + * bullet. The report is produced for a human but may also be parsed/rendered + * by static site generators, so we defang the characters most likely to + * break structure or smuggle markdown: + * - newlines / carriage returns (can introduce fake headings, bullets, HR) + * - backticks (can open/close code fences) + * - square brackets (can form `[text](url)` auto-links) + * We also collapse runs of whitespace so long blobs do not distort columns. + */ +function sanitizePreview(s) { + if (!s) return ""; + return String(s) + .replace(/[\r\n]+/g, " ") + .replace(/[`\[\]]/g, "") + .replace(/\s+/g, " ") + .trim(); +} + +/** + * Resolve `--report` against the current working directory and reject paths + * that escape it. Absolute paths and relative paths that resolve outside + * `cwd` (e.g. `--report=../../etc/passwd`) are refused. This is a self-harm + * guard, not a security boundary — anyone running the script can pick any + * path, but a typo or copy-pasted flag should not overwrite system files. + */ +function resolveReportPath(raw) { + const cwd = process.cwd(); + const resolved = path.resolve(cwd, raw); + // path.relative returns "" when paths match. Any segment starting with ".." + // (or an absolute path on Windows/POSIX) signals traversal out of cwd. + const rel = path.relative(cwd, resolved); + if (rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel))) { + return resolved; + } + throw new Error( + `--report path must stay inside the current working directory. ` + + `Got "${raw}" which resolves to "${resolved}" (outside "${cwd}").` + ); +} + // ── Tier 1: SQL-only lint (free) ──────────────────────────────────────────── // // Each check pulls from `public.thoughts` via PostgREST with aggregation @@ -361,7 +408,7 @@ async function tier2GraphLint(db, args) { id: t.id, importance: t.importance, created_at: t.created_at, - preview: String(t.content || "").slice(0, 120), + preview: sanitizePreview(String(t.content || "").slice(0, 120)), }); } } From 2787ae0b39bb8dbf7e9e94f703985e04fde7b799 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:28:24 -0400 Subject: [PATCH 017/125] [recipes] Defer WR-04, IN-03: add TODO comments for documented trade-offs --- recipes/lint-sweep/lint-sweep.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index 609823bf9..52e353186 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -226,6 +226,11 @@ function makeRestClient(baseUrl, serviceKey) { const cr = res.headers.get("content-range") || ""; const m = cr.match(/\/(\d+|\*)/); if (m && m[1] !== "*") return Number(m[1]); + // TODO(IN-03): If Content-Range is missing entirely (some proxies strip + // it on otherwise-OK responses) we fall through to reading the body here, + // which may already have been consumed above on the error path. On a + // successful response this silently returns 0. Low-risk cosmetic; kept + // as-is for now because PostgREST proper always sets the header. const arr = await res.json().catch(() => []); return Array.isArray(arr) ? arr.length : 0; } @@ -567,6 +572,14 @@ async function tier3LlmLint(db, args) { try { parsed = JSON.parse(cleaned); } catch (e) { + // TODO(WR-04): Currently any single unparseable LLM response aborts the + // whole run and no partial report is written (writeFileSync happens + // after every tier completes). This is documented fail-loud behavior + // (README "Safety" section). Future options if this becomes painful: + // 1) retry once with a stricter system prompt, + // 2) write a partial report before re-raising, + // 3) skip the failing batch and continue. + // Left as documented trade-off for now. throw new Error(`Failed to parse Tier 3 JSON (call ${out.llmCalls}): ${e.message}\nRaw: ${raw.slice(0, 300)}`); } From c49a5eef1aaab125145f05a8fe45f0e6e177bab0 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:28:29 -0400 Subject: [PATCH 018/125] [recipes] Fix WR-03/WR-05: service-key warning, .env.example, date-range fix --- recipes/weekly-digest/.env.example | 33 +++++++++++++++++++++++++ recipes/weekly-digest/README.md | 3 +++ recipes/weekly-digest/weekly-digest.mjs | 6 ++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 recipes/weekly-digest/.env.example diff --git a/recipes/weekly-digest/.env.example b/recipes/weekly-digest/.env.example new file mode 100644 index 000000000..2337990f0 --- /dev/null +++ b/recipes/weekly-digest/.env.example @@ -0,0 +1,33 @@ +# Weekly Digest — environment template +# ----------------------------------------------------------------------------- +# Copy to .env.local (gitignored) and fill in, then load with dotenv-cli: +# npx dotenv -e .env.local -- node weekly-digest.mjs +# The script itself does not read .env files; the runner (cron, systemd, +# GitHub Actions, dotenv-cli) must inject them into the environment. + +# ─── Required: Open Brain (Supabase) connection ────────────────────────────── +# Canonical names — shared with every other recipe in recipes/. +SUPABASE_URL=https://your-project-ref.supabase.co +SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here + +# Legacy aliases (accepted for back-compat, will print a deprecation warning): +# OPEN_BRAIN_URL=https://your-project-ref.supabase.co +# OPEN_BRAIN_SERVICE_KEY=your-service-role-key-here + +# ─── Required: LLM credential — pick ONE ───────────────────────────────────── +# Preferred: direct Anthropic API (cleaner billing, faster path). +ANTHROPIC_API_KEY=sk-ant-... + +# Fallback: OpenRouter (useful if you don't have an Anthropic account yet). +# OPENROUTER_API_KEY=sk-or-... + +# ─── Optional: Telegram delivery ───────────────────────────────────────────── +# Needed only when --output=telegram (the default). Skip these if you plan +# to use --output=stdout or --output=file. +TELEGRAM_BOT_TOKEN=123456:ABCDEF... +TELEGRAM_CHAT_ID=987654321 + +# ─── Optional: model override ──────────────────────────────────────────────── +# Accepts a model id (e.g., claude-opus-4-7) or an alias (opus|sonnet|haiku). +# Command-line --model= takes precedence over this. +# DIGEST_MODEL=claude-opus-4-7 diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md index b35e07586..c48ecfc89 100644 --- a/recipes/weekly-digest/README.md +++ b/recipes/weekly-digest/README.md @@ -27,6 +27,9 @@ This is a "consumption format" companion to your capture habit. Captures alone p - **Optional:** a Telegram bot for delivery. If you don't have one, use `--output=stdout` or `--output=file` and skip the Telegram setup entirely. - **Required for the out-of-the-box safety guarantee:** a `sensitivity_tier TEXT` column on `public.thoughts`. No official Open Brain primitive ships this yet; if you haven't added the column, either install your own migration or wait for the sensitivity-tiers primitive to land upstream. On stock OB1, the recipe **fails closed** — it refuses to run and tells you how to proceed (see the Sensitivity section below). If you explicitly accept the data-leakage risk, pass `--no-sensitivity-filter` to run unfiltered. +> [!WARNING] +> This recipe uses your Supabase **service role key** (`SUPABASE_SERVICE_ROLE_KEY`). That key **bypasses Row Level Security entirely** — the script can read every row in `public.thoughts` regardless of your RLS policies. On an install without `sensitivity_tier` (or with it misconfigured), that means every capture in the window is eligible to be shipped to the LLM provider and your Telegram chat. Treat those two endpoints as extensions of your brain's trust boundary, and do not run this recipe against a brain that holds material you don't want exfiltrated unless the sensitivity tagging is in place. + ## Credential Tracker Copy this block into a text editor and fill it in as you go. diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 45b1dd245..9caae2926 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -464,7 +464,11 @@ async function synthesizeOpenRouter(cfg, model, systemPrompt, userPrompt) { async function synthesize(cfg, thoughts, windowDays, model) { const endDate = new Date(); - const startDate = new Date(Date.now() - (windowDays - 1) * 86_400_000); + // Match the fetch window exactly: fetchThoughts computes `sinceIso` as + // `now - windowDays * 86_400_000` (e.g., --window=7 ⇒ 7×24h lookback). + // The printed digest header must reflect that same span, otherwise + // "Apr 11 – Apr 17" misrepresents a 7-day fetch as a 6-day span. + const startDate = new Date(Date.now() - windowDays * 86_400_000); const userPrompt = buildUserPrompt(thoughts, startDate, endDate); const text = From b097137b69facf7a39186590fbece276b6e08d07 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 09:29:22 -0400 Subject: [PATCH 019/125] [recipes] Fix IN-03: chunker paragraph boundaries with word-boundary fallback --- recipes/weekly-digest/weekly-digest.mjs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 9caae2926..6dae66e73 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -484,16 +484,27 @@ async function synthesize(cfg, thoughts, windowDays, model) { /** * Telegram text messages cap at 4096 chars. If the digest goes over, split - * on paragraph boundaries so we don't chop mid-bullet. + * cleanly: prefer a paragraph break (double newline), then a single newline, + * then a word boundary (space), then — only as a last resort — a hard cut. + * + * The tiered fallback matters when a single paragraph is itself longer than + * the chunk limit: without the word-boundary step we'd hard-cut mid-word + * and produce ugly output. The `< limit * 0.5` guard avoids making a tiny + * chunk when the best boundary is near the start of the window. */ function chunkForTelegram(text, limit = TELEGRAM_CHUNK_LIMIT) { if (text.length <= limit) return [text]; const chunks = []; let remaining = text; while (remaining.length > limit) { - // Prefer to break on a double newline (paragraph), else single newline. + // 1. Paragraph boundary (preferred). let cut = remaining.lastIndexOf("\n\n", limit); + // 2. Single-newline boundary. if (cut < limit * 0.5) cut = remaining.lastIndexOf("\n", limit); + // 3. Word boundary — catches the "one long paragraph" edge case where the + // LLM emits a wall of prose with no \n inside the chunk limit. + if (cut < limit * 0.5) cut = remaining.lastIndexOf(" ", limit); + // 4. Hard cut — last resort, only if nothing useful was found. if (cut < limit * 0.5) cut = limit; chunks.push(remaining.slice(0, cut).trimEnd()); remaining = remaining.slice(cut).trimStart(); From 0b26574560ba8fd58acce9fd93f8a5b98f292a6f Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 14:11:03 -0400 Subject: [PATCH 020/125] [recipes] Fix REVIEW-CODEX-2-P1: report path accepts absolute and home-expanded paths --- recipes/lint-sweep/lint-sweep.js | 59 ++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index 52e353186..244490064 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -47,6 +47,7 @@ */ import fs from "node:fs"; +import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -129,8 +130,8 @@ function parseArgs(argv) { else if (a === "--sample-size") args.sampleSize = parseNumberFlag(argv[++i], 100); else if (a.startsWith("--max-llm-calls=")) args.maxLlmCalls = parseNumberFlag(a.slice(16), 5); else if (a === "--max-llm-calls") args.maxLlmCalls = parseNumberFlag(argv[++i], 5); - else if (a.startsWith("--report=")) args.report = a.slice(9); - else if (a === "--report") args.report = argv[++i]; + else if (a.startsWith("--report=")) args.report = expandHome(a.slice(9)); + else if (a === "--report") args.report = expandHome(argv[++i]); else if (a.startsWith("--days=")) args.days = parseNumberFlag(a.slice(7), 365); else if (a === "--days") args.days = parseNumberFlag(argv[++i], 365); else if (a.startsWith("--llm-model=")) args.llmModel = a.slice(12); @@ -154,9 +155,11 @@ function parseArgs(argv) { const date = new Date().toISOString().slice(0, 10); args.report = path.join(process.cwd(), `lint-report-${date}.md`); } - // Refuse report paths that escape cwd — stops typos like `--report=../x` - // from silently overwriting unrelated files. The default above is already - // under cwd, so only user-supplied values can fail this check. + // Expand `~/` and refuse relative paths that climb out of cwd. Absolute + // paths (including `~/lint-reports/...`, `/tmp/...`, `C:/tmp/...`) are + // accepted so scheduled jobs can write outside the repo. The default above + // is already absolute and under cwd, so only user-supplied values are + // rewritten here. args.report = resolveReportPath(args.report); return args; } @@ -260,24 +263,50 @@ function sanitizePreview(s) { } /** - * Resolve `--report` against the current working directory and reject paths - * that escape it. Absolute paths and relative paths that resolve outside - * `cwd` (e.g. `--report=../../etc/passwd`) are refused. This is a self-harm - * guard, not a security boundary — anyone running the script can pick any - * path, but a typo or copy-pasted flag should not overwrite system files. + * Expand a leading `~/` (or bare `~`) to the current user's home directory. + * Only the prefix form is handled — embedded `~` elsewhere in the string is + * treated as a literal character, matching shell-style behaviour. + */ +function expandHome(raw) { + if (typeof raw !== "string" || raw.length === 0) return raw; + if (raw === "~") return os.homedir(); + if (raw.startsWith("~/") || raw.startsWith("~\\")) { + return path.join(os.homedir(), raw.slice(2)); + } + return raw; +} + +/** + * Resolve `--report` and reject only true directory traversal. Absolute paths + * (home-expanded, temp dirs, Windows drive paths) are accepted so scheduled + * jobs can write outside `cwd` — this is a self-harm guard, not a security + * boundary. We still refuse relative paths whose resolved form climbs out of + * `cwd` (e.g. `--report=../../etc/passwd`) because that almost always means a + * typo or copy-pasted flag. */ function resolveReportPath(raw) { + const expanded = expandHome(raw); const cwd = process.cwd(); - const resolved = path.resolve(cwd, raw); - // path.relative returns "" when paths match. Any segment starting with ".." - // (or an absolute path on Windows/POSIX) signals traversal out of cwd. + const resolved = path.resolve(cwd, expanded); + + // Absolute inputs (after `~/` expansion) are trusted — the user stated an + // explicit path. Only relative inputs need traversal validation. + if (path.isAbsolute(expanded)) { + return resolved; + } + const rel = path.relative(cwd, resolved); + // A relative input is safe when the resolved path stays inside cwd. That + // means `path.relative` returns either "" or a non-empty string that does + // NOT start with ".." and is NOT itself absolute (rare cross-drive case on + // Windows, where path.relative can return an absolute path). if (rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel))) { return resolved; } throw new Error( - `--report path must stay inside the current working directory. ` + - `Got "${raw}" which resolves to "${resolved}" (outside "${cwd}").` + `--report path must not traverse above the current working directory. ` + + `Got "${raw}" which resolves to "${resolved}" (outside "${cwd}"). ` + + `Use an absolute path (e.g. /tmp/report.md or ~/lint-reports/report.md) instead.` ); } From 83f63df9db8d16e0ac16e4e52aa05d53dae3484e Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 14:11:42 -0400 Subject: [PATCH 021/125] [recipes] Fix REVIEW-CODEX-2-P1: Tier 3 honors --sample-size instead of hard 500 cap --- recipes/lint-sweep/lint-sweep.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/recipes/lint-sweep/lint-sweep.js b/recipes/lint-sweep/lint-sweep.js index 244490064..c5955f79c 100644 --- a/recipes/lint-sweep/lint-sweep.js +++ b/recipes/lint-sweep/lint-sweep.js @@ -498,12 +498,17 @@ async function tier3LlmLint(db, args) { return out; } - // Pull a recent sample of atomic (non-derived) thoughts + // Pull a recent sample of atomic (non-derived) thoughts. Fetch 2x the + // requested sample so the post-filter `.slice(0, args.sampleSize)` below + // still has enough eligible rows after derived/short thoughts are dropped. + // The upper bound mirrors the `--sample-size` validator (max 1000) so we + // never silently under-sample when the user asks for 500+ thoughts. const since = new Date(Date.now() - args.days * 86_400_000).toISOString(); + const fetchLimit = Math.min(args.sampleSize * 2, 1000); const rows = await db.get( `/thoughts?select=id,content,importance,type,source_type,created_at,metadata` + `&created_at=gte.${encodeURIComponent(since)}` + - `&order=id.desc&limit=${Math.min(args.sampleSize * 2, 500)}` + `&order=id.desc&limit=${fetchLimit}` ); const atomic = rows.filter( (t) => t?.metadata?.derivation_layer !== "derived" && typeof t.content === "string" && t.content.trim().length >= 20 From fcb0ee3b24c2a332638550377bf8233c75306d50 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 14:12:11 -0400 Subject: [PATCH 022/125] [recipes] Fix REVIEW-CODEX-2-P2: correct README partial-report claim --- recipes/lint-sweep/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index 15fbd39cb..df5498196 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -214,7 +214,7 @@ Tier 3 is the only billed component. Using the default `anthropic/claude-haiku-4 | 500 | 25 | ~$0.10 | | 1000 | 50 | ~$0.20 | -Pick a smaller, faster model (`anthropic/claude-haiku-4-5`) for cheap sweeps or a stronger one (`anthropic/claude-sonnet-4-5`) for weekly deep audits. The script does not retry on failure — a bad response aborts the run and leaves you with a partial report rather than silently burning credits. +Pick a smaller, faster model (`anthropic/claude-haiku-4-5`) for cheap sweeps or a stronger one (`anthropic/claude-sonnet-4-5`) for weekly deep audits. The script does not retry on failure — a Tier 3 parse failure aborts the run before any report is written, so you get no file at all rather than silently burning credits on a flaky model. If you want Tier 1/2 output without any Tier 3 risk, run with `--max-llm-calls=0` (Tier 3 skips with a logged reason and the report is still written). Set `--max-llm-calls=0` to disable Tier 3 explicitly without needing to edit the tier flag, and omit the `OPENROUTER_API_KEY` entirely to make Tier 3 skip with a logged reason. From 4cfb399b19fbe25144d2492eba31eb3d64830787 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 14:13:30 -0400 Subject: [PATCH 023/125] [recipes] Fix REVIEW-CODEX-2-P2: honor native importance column when present --- recipes/weekly-digest/weekly-digest.mjs | 125 +++++++++++++++++------- 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 6dae66e73..4783dd4cf 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -247,15 +247,21 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi // column won't trip the 400. Otherwise, apply the exclusion filter. // // NOTE on importance: stock OB1 `public.thoughts` does NOT have an - // `importance` column. This recipe reads it from `metadata->>'importance'` - // on the client side (see `thoughtImportance()` below), so we do not select - // a bare `importance` column here. Installs that do add a native - // `importance` column can expose it via `metadata.importance` in their - // capture pipeline, or future work can add a COALESCE path that prefers - // the native column when present. - const selectCols = noSensitivityFilter - ? "id,content,created_at,metadata" - : "id,content,created_at,metadata,sensitivity_tier"; + // `importance` column, but enhanced-schema installs DO. We optimistically + // include `importance` in the select and, if PostgREST 400s with "column + // does not exist", transparently retry without it and memoize the result + // on the cfg object so subsequent pages skip the probe. This lets + // `thoughtImportance()` honor a real native column when present and fall + // back to `metadata.importance` on stock OB1. See the column-probe block + // in the fetch loop for the retry logic. + const buildSelect = (withImportance) => + withImportance + ? (noSensitivityFilter + ? "id,content,created_at,metadata,importance" + : "id,content,created_at,metadata,sensitivity_tier,importance") + : (noSensitivityFilter + ? "id,content,created_at,metadata" + : "id,content,created_at,metadata,sensitivity_tier"); const excludeFilter = noSensitivityFilter ? "" : `&sensitivity_tier=not.in.(${excluded.join(",")})`; @@ -276,27 +282,60 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi }; const all = []; + // Memoize native `importance` column presence across pages. Start by + // assuming it exists (optimistic), flip to false on the first 400 that + // names the column, and cache on cfg so every subsequent page — and any + // future run that reuses this cfg — skips the probe. + let withImportance = cfg.hasNativeImportance !== false; for (let offset = 0; offset < FETCH_HARD_CAP; offset += PAGE_SIZE) { const limit = Math.min(PAGE_SIZE, FETCH_HARD_CAP - offset); - const url = - `${cfg.baseUrl}/rest/v1/thoughts` + - `?select=${selectCols}` + - `&created_at=gte.${sinceIso}` + - `&order=created_at.desc` + - `&limit=${limit}&offset=${offset}` + - excludeFilter; - - const res = await fetch(url, { headers }); - - // If sensitivity_tier column doesn't exist on this install, PostgREST - // returns 400 with "column does not exist". FAIL CLOSED: do not retry - // unfiltered, because that would silently leak restricted/personal - // thoughts on a brain that relies on the primitive's promise. Print a - // clear error and instruct the user how to proceed. - if (!res.ok && !noSensitivityFilter && res.status === 400) { + + const doFetch = (useImportance) => { + const url = + `${cfg.baseUrl}/rest/v1/thoughts` + + `?select=${buildSelect(useImportance)}` + + `&created_at=gte.${sinceIso}` + + `&order=created_at.desc` + + `&limit=${limit}&offset=${offset}` + + excludeFilter; + return fetch(url, { headers }); + }; + + let res = await doFetch(withImportance); + + // Error path: consume the body ONCE and branch on its content. We can't + // read .text() twice on the same Response, so we inspect it here and + // route to the importance-probe retry, the sensitivity fail-closed exit, + // or a generic throw. On success this block is skipped entirely. + if (!res.ok) { const text = await res.text(); - if (/sensitivity_tier/.test(text)) { + + // Optimistic importance probe: if this install lacks a native + // `importance` column, drop it from the select, memoize the result, + // and retry once. We match both the column name and "column... does + // not exist" wording to avoid mis-reacting to unrelated 400s. + if ( + withImportance && + res.status === 400 && + /\bimportance\b/.test(text) && + /column|does not exist/i.test(text) + ) { + withImportance = false; + cfg.hasNativeImportance = false; + res = await doFetch(false); + if (!res.ok) { + throw new Error(`thoughts fetch failed: ${res.status} ${await res.text()}`); + } + } else if ( + !noSensitivityFilter && + res.status === 400 && + /sensitivity_tier/.test(text) + ) { + // Sensitivity column missing on this install. FAIL CLOSED: do not + // retry unfiltered, because that would silently leak restricted/ + // personal thoughts on a brain that relies on the primitive's + // promise. Print a clear error and instruct the user how to proceed. console.error( "[weekly-digest] FATAL: sensitivity_tier column not found on public.thoughts.\n" + "\n" + @@ -315,12 +354,9 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi "PostgREST error detail: " + text, ); process.exit(1); + } else { + throw new Error(`thoughts fetch failed: ${res.status} ${text}`); } - throw new Error(`thoughts fetch failed: ${res.status} ${text}`); - } - - if (!res.ok) { - throw new Error(`thoughts fetch failed: ${res.status} ${await res.text()}`); } const batch = await res.json(); @@ -329,21 +365,36 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi if (batch.length < limit) break; } + // Cache for any helpers that peek at cfg later. Default to true when we + // never had to flip the flag (the column was there, or the table was + // empty so we never got a 400 to prove otherwise). + if (cfg.hasNativeImportance === undefined) { + cfg.hasNativeImportance = withImportance; + } + return all; } /** - * Read the importance score for a thought. Stock OB1 has no `importance` - * column on public.thoughts; capture pipelines that score importance stash - * it under `metadata.importance`. We prefer a native top-level `importance` - * if a given install happens to have one (via COALESCE-style logic), then - * fall back to `metadata.importance`, then 0. + * Read the importance score for a thought. Enhanced-schema installs expose + * a native `importance` column on public.thoughts; stock OB1 does not. + * `fetchThoughts` optimistically selects the column, retries without it on + * the "column does not exist" 400, and memoizes the result. This helper + * prefers the native top-level value when present (via COALESCE-style + * logic), then falls back to `metadata.importance`, then 0 — so both + * schemas produce correct rankings without any caller-side flag. * - * Accepts number or numeric string in metadata (JSON round-trip safety). + * Accepts number or numeric string in either location (JSON round-trip + * safety, since metadata comes back as parsed JSON but some capture + * pipelines stringify the field). */ function thoughtImportance(t) { const native = t?.importance; if (typeof native === "number" && Number.isFinite(native)) return native; + if (typeof native === "string") { + const n = Number(native); + if (Number.isFinite(n)) return n; + } const m = t?.metadata?.importance; if (typeof m === "number" && Number.isFinite(m)) return m; if (typeof m === "string") { From 40abca0083dd830287f87e4aa73f993eb6ef4f25 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 14:14:24 -0400 Subject: [PATCH 024/125] [recipes] Fix REVIEW-CODEX-2-P2: include NULL sensitivity_tier rows in digest filter --- recipes/weekly-digest/README.md | 2 +- recipes/weekly-digest/weekly-digest.mjs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/recipes/weekly-digest/README.md b/recipes/weekly-digest/README.md index c48ecfc89..2a4742879 100644 --- a/recipes/weekly-digest/README.md +++ b/recipes/weekly-digest/README.md @@ -127,7 +127,7 @@ This recipe expects a `sensitivity_tier TEXT` column on `public.thoughts` with v - **`restricted`** — never included. These are thoughts you've explicitly flagged as off-limits; a synthesis pass that surfaces them to a Telegram chat defeats the purpose of the tier. - **`personal`** — excluded by default. Your week probably contains private material (health, relationships, finances) that you'd rather not summarize over an unencrypted wire to a third-party API. Pass `--include-personal` when you want the fuller picture — e.g., a private file output you keep locally. -- **`standard`** (or null) — always included. +- **`standard`** (or `NULL`) — always included. The filter uses a PostgREST `or=(sensitivity_tier.is.null, sensitivity_tier.not.in.(...))` composite so rows with `NULL` tiers are explicitly unioned in. (Postgres `NOT IN` alone silently drops `NULL` rows, which would hide untagged thoughts on an install that added the column without backfilling old rows.) **Fail-closed behavior on stock OB1:** if the `sensitivity_tier` column is not present (e.g., a vanilla Open Brain install), the recipe **refuses to run** and prints instructions. It does not silently fall back to an unfiltered query, because that would violate the guarantee below. To override this safety net — at your own risk — pass `--no-sensitivity-filter`; the script will print a loud warning and send every row in the window to the LLM and delivery target. diff --git a/recipes/weekly-digest/weekly-digest.mjs b/recipes/weekly-digest/weekly-digest.mjs index 4783dd4cf..053bf7083 100644 --- a/recipes/weekly-digest/weekly-digest.mjs +++ b/recipes/weekly-digest/weekly-digest.mjs @@ -262,9 +262,17 @@ async function fetchThoughts(cfg, { windowDays, includePersonal, noSensitivityFi : (noSensitivityFilter ? "id,content,created_at,metadata" : "id,content,created_at,metadata,sensitivity_tier"); + // PostgREST OR filter: include rows with NULL sensitivity_tier AND rows + // whose tier is not in the excluded set. A bare `not.in.(...)` follows + // Postgres NOT IN semantics, which silently drops NULL rows — that would + // hide every untagged thought on installs that added the column without + // backfilling old rows to 'standard'. The README promises NULL/standard + // is included, so we union the two branches here. Values inside the + // `in.(...)` list are not quoted in PostgREST's compact filter syntax; + // keep `excluded` as bare lowercase identifiers that need no escaping. const excludeFilter = noSensitivityFilter ? "" - : `&sensitivity_tier=not.in.(${excluded.join(",")})`; + : `&or=(sensitivity_tier.is.null,sensitivity_tier.not.in.(${excluded.join(",")}))`; if (noSensitivityFilter) { console.warn( From 09dffdec225c27322575225b57202ca039e9b204 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 15:18:04 -0400 Subject: [PATCH 025/125] [recipes] Fix REVIEW-CODEX-3-P3: refresh README sample output to match current generator --- recipes/lint-sweep/README.md | 49 +++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index df5498196..7339a74cf 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -130,26 +130,38 @@ The output is a self-contained markdown file ready for human review. Sample outp ```markdown --- -title: Lint Sweep — 2026-04-17 -generated_at: 2026-04-17T14:22:11.031Z +title: Lint Sweep — 2026-04-18 +generated_at: 2026-04-18T14:22:11.031Z tier: all -started_at: 2026-04-17T14:22:04.112Z -finished_at: 2026-04-17T14:22:11.031Z +started_at: 2026-04-18T14:22:04.112Z +finished_at: 2026-04-18T14:22:11.031Z --- -# Open Brain Lint Sweep — 2026-04-17 +# Open Brain Lint Sweep — 2026-04-18 *Read-only audit. This script never mutates thoughts.* +## Scan scope + +This run inspects bounded samples, not your entire brain. Counts below are relative to these samples. + +- **Tier 1** — most recent **2000 thoughts** (ordered by `id desc`) for orphan/over-tag/length checks; up to **5000 rows** with a populated `content_fingerprint` for duplicate detection; full-table exact row counts for `thoughts` and `content_fingerprint IS NULL` (no cap). +- **Tier 2** — first **500 high-importance thoughts** (`importance >= 4`), first **2000 entities**, first **5000 edges**. +- **Tier 3** — up to **100 thoughts** from the last **365 days**, batched ~20 per LLM call, hard-capped at **5 LLM calls**. + +On brains larger than these caps, Tier 1/2 counts represent a **slice**, not the global total. Example: "Entities with zero edges: 12" under a 2000-entity cap means *12 isolated entities among the first 2000 returned*, not "12 total isolated entities." For whole-brain coverage, run the SQL views in [`views.sql`](./views.sql) directly. + ## Summary -- Total thoughts inspected (Tier 1 sample): 12847 -- Orphans by tag (recent 2000): 43 -- Exact-duplicate fingerprint groups: 2 -- Rows missing content_fingerprint: 0 -- Low-signal noise candidates: 18 -- High-importance isolated (no entity links): 7 -- Entities with zero edges: 12 +*Counts below reflect the bounded scan scope described above — not whole-brain totals.* + +- Total thoughts in table (exact count, uncapped): 12847 +- Orphans by tag (in recent 2000 sampled): 43 +- Exact-duplicate fingerprint groups (in first 5000 fingerprinted rows): 2 +- Rows missing content_fingerprint (exact count, uncapped): 0 +- Low-signal noise candidates (in recent 2000 sampled): 18 +- High-importance isolated — no entity links (in first 500 high-importance sampled): 7 +- Entities with zero edges (among first 2000 entities ∩ first 5000 edges): 12 - LLM contradiction findings: 4 (over 100 thoughts, 5 LLM calls) ## Tier 1 — SQL-only lint (free) @@ -158,14 +170,20 @@ finished_at: 2026-04-17T14:22:11.031Z - Over-tagged (>10 tags): **3** — typically import noise. - thought #48221 → 14 tags - thought #48219 → 12 tags +- Empty content: **0** +- Very long content (>20K chars): **2** — usually unchunked dumps. - Low-signal noise (importance ≤2, content <40 chars): **18** - Exact-duplicate fingerprint groups: **2** - fingerprint a1b2c3d4e5f6… → 2 copies (ids: 11032, 11418) +- Rows missing content_fingerprint: **0** — consider running the fingerprint-dedup-backfill recipe. ## Tier 2 — Graph-based lint (free) -- High-importance (≥4) thoughts with no entity links: **7** +*Scope: first 500 high-importance thoughts, first 2000 entities, first 5000 edges. Counts below are within that slice, not the whole brain.* + +- High-importance (≥4) thoughts with no entity links (in first 500 high-importance sampled): **7** - #12033 (imp=5, 2026-01-14) — Moving biweekly 1:1 from Thursday to Tuesday starting next month… +- Entities with zero edges (among first 2000 entities ∩ first 5000 edges): **12** ## Tier 3 — LLM-assisted contradiction sampling (budgeted) @@ -185,10 +203,7 @@ finished_at: 2026-04-17T14:22:11.031Z --- -**Safety:** `lint-sweep.js` is read-only. Every finding above is a suggestion -for a human to review. Before acting on any item, verify the thought with -`get_thought` or the web UI. Never delete or edit a thought based solely on -this report. +**Safety:** `lint-sweep.js` is read-only. Every finding above is a suggestion for a human to review. Before acting on any item, verify the thought with `get_thought` or the web UI. Never delete or edit a thought based solely on this report. ``` ## Expected Outcome From 0ea12962cea036caa9fab77f77b9aeb1b3ba6721 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:43:27 -0400 Subject: [PATCH 026/125] [recipes] Fix CI Rule 13: convert broken relative links to external PR URLs --- recipes/lint-sweep/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index 7339a74cf..27e1dd014 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -282,4 +282,4 @@ Solution: Run with `--verbose` to see the raw response. Switch to a stronger mod - **[content-fingerprint-dedup](../content-fingerprint-dedup/)** — installs the `content_fingerprint` column Tier 1 needs for duplicate detection. - **[fingerprint-dedup-backfill](../fingerprint-dedup-backfill/)** — backfills fingerprints on pre-existing rows so Tier 1 duplicate scanning is accurate. - **`entity-extraction` schema** — installs the `entities`, `edges`, and `thought_entities` tables Tier 2 walks. See PRs [#197](https://github.com/NateBJones-Projects/OB1/pull/197) and [#199](https://github.com/NateBJones-Projects/OB1/pull/199). (The `ob-graph` recipe is a separate build with different table names and is NOT compatible with Tier 2.) -- **[thought-enrichment](../thought-enrichment/)** — populates `metadata.topics`, `metadata.tags`, and `metadata.people` so Tier 1 orphan-by-tag detection is meaningful. +- **[thought-enrichment recipe (PR #192)](https://github.com/NateBJones-Projects/OB1/pull/192)** — populates `metadata.topics`, `metadata.tags`, and `metadata.people` so Tier 1 orphan-by-tag detection is meaningful. From 54d29567927cea01c22ec7f3a732a9db96546513 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:44:07 -0400 Subject: [PATCH 027/125] [schemas] Text search trigram index for ILIKE fallback --- schemas/text-search-trgm/README.md | 112 +++++++++++++++++++++++++ schemas/text-search-trgm/metadata.json | 20 +++++ schemas/text-search-trgm/schema.sql | 39 +++++++++ 3 files changed, 171 insertions(+) create mode 100644 schemas/text-search-trgm/README.md create mode 100644 schemas/text-search-trgm/metadata.json create mode 100644 schemas/text-search-trgm/schema.sql diff --git a/schemas/text-search-trgm/README.md b/schemas/text-search-trgm/README.md new file mode 100644 index 000000000..117671b8a --- /dev/null +++ b/schemas/text-search-trgm/README.md @@ -0,0 +1,112 @@ +# Text Search Trigram Index + +> Adds a `pg_trgm` GIN index on `public.thoughts.content` so `search_thoughts_text` ILIKE fallback queries run in ~150ms instead of ~8s. + +## What It Does + +Installs the `pg_trgm` extension and creates a trigram GIN index on `public.thoughts.content`. The `search_thoughts_text` RPC from the enhanced-thoughts schema runs a tsvector phase first, then falls back to `ILIKE '%query%'` whenever tsvector returns fewer hits than requested -- which happens for most real-world queries. Without a trigram index that fallback sequential-scans the entire table. + +**Before/after (89K-thought brain):** + +| Query | Before | After | +|-------|--------|-------| +| Rare-word ILIKE fallback | ~8s (seq scan) | ~100-150ms (bitmap index scan) | +| Common-word tsvector hit | unchanged | unchanged | + +## Why It Matters + +`search_thoughts_text` powers every text search that goes through Open Brain's MCP layer. Leading-wildcard patterns like `ILIKE '%foo%'` cannot use the existing tsvector GIN index (tsvector is word-level, ILIKE is substring-level), so the planner defaults to a full sequential scan. At ~90K rows that's a 7-8 second wait on every rare-word lookup. + +`pg_trgm` breaks text into 3-character trigrams and builds a GIN index the planner *can* use for substring matching. No changes to `search_thoughts_text` are needed -- the planner picks up the new index automatically. Queries that previously seq-scanned now run as bitmap index scans. + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) +- [`schemas/enhanced-thoughts`](https://github.com/NateBJones-Projects/OB1/pull/191) installed (defines `search_thoughts_text` and the base tsvector index) +- Supabase project with write access to run migrations + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +TEXT SEARCH TRIGRAM INDEX -- CREDENTIAL TRACKER +-------------------------------------- + +SUPABASE (from your Open Brain setup) + Project URL: ____________ + Secret key: ____________ + +-------------------------------------- +``` + +## Steps + +1. Open your Supabase dashboard and navigate to the **SQL Editor** +2. Create a new query and paste the full contents of `schema.sql` +3. Click **Run** to execute the migration (the `CREATE INDEX` will briefly lock the `thoughts` table against writes; ~1-2 minutes at 90K rows) +4. Navigate to **Database > Extensions** and confirm `pg_trgm` is enabled +5. Navigate to **Database > Indexes** (or run the verification query below) and confirm `idx_thoughts_content_trgm` exists on `public.thoughts` + +## Expected Outcome + +After running the migration: + +- The `pg_trgm` extension is installed in the database. +- A GIN trigram index named `idx_thoughts_content_trgm` exists on `public.thoughts(content)`. +- The next `search_thoughts_text` call whose ILIKE fallback fires will complete in ~100-150ms instead of ~8s. + +## Verification + +Run the following in the SQL Editor. The `Bitmap Index Scan on idx_thoughts_content_trgm` line in the plan confirms the planner is using the new index: + +```sql +EXPLAIN ANALYZE +SELECT id +FROM public.thoughts +WHERE content ILIKE '%somerarewordfromyourbrain%' +LIMIT 25; +``` + +Expected plan (abbreviated): + +``` +Limit + -> Bitmap Heap Scan on thoughts + Recheck Cond: (content ~~* '%somerarewordfromyourbrain%'::text) + -> Bitmap Index Scan on idx_thoughts_content_trgm + Index Cond: (content ~~* '%somerarewordfromyourbrain%'::text) +Execution Time: ~100-200 ms +``` + +If you instead see `Seq Scan on thoughts`, the index was not created or the planner has stale statistics -- run `ANALYZE public.thoughts;` and try again. + +## Rollback + +```sql +DROP INDEX IF EXISTS public.idx_thoughts_content_trgm; +``` + +The `pg_trgm` extension is left installed; it is harmless on its own and may be used by other contributions. + +## Tradeoffs + +- **Storage:** ~20-40MB on a 90K-thought brain. Scales linearly with total content size. +- **Build lock:** Regular (non-CONCURRENT) `CREATE INDEX` briefly locks `public.thoughts` against writes during the build (~1-2 minutes at 90K rows). If you're running live capture and can't tolerate a brief write pause, switch the statement to `CREATE INDEX CONCURRENTLY` and remove the surrounding `BEGIN/COMMIT` -- concurrent index builds cannot run inside a transaction. +- **Write amplification:** Small per-row overhead on `INSERT` and `UPDATE` of `content` (the index needs to be maintained). Imperceptible at typical personal-brain write rates. + +## Troubleshooting + +**Issue: "extension pg_trgm does not exist" error** +Solution: Your Supabase project predates automatic extension availability. In the SQL Editor, run `CREATE EXTENSION pg_trgm;` as a superuser or contact Supabase support. The migration uses `CREATE EXTENSION IF NOT EXISTS`, which works on all current Supabase projects. + +**Issue: `EXPLAIN ANALYZE` still shows `Seq Scan on thoughts`** +Solution: Run `ANALYZE public.thoughts;` to refresh planner statistics, then retry. The planner needs accurate row counts before it will choose an index scan over a seq scan on small tables. + +**Issue: Migration hangs on `CREATE INDEX`** +Solution: Check for long-running transactions holding locks on `thoughts` (look at `pg_stat_activity`). The index build needs to acquire a `SHARE` lock on the table. If you can't stop the blocking transaction, switch to `CREATE INDEX CONCURRENTLY` (see Tradeoffs). + +## References + +- [PostgreSQL `pg_trgm` documentation](https://www.postgresql.org/docs/current/pgtrgm.html) -- official reference for trigram matching and index operator classes +- [`schemas/enhanced-thoughts`](../enhanced-thoughts/README.md) -- defines `search_thoughts_text`, the consumer of this index diff --git a/schemas/text-search-trgm/metadata.json b/schemas/text-search-trgm/metadata.json new file mode 100644 index 000000000..0a24c7b42 --- /dev/null +++ b/schemas/text-search-trgm/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Text Search Trigram Index", + "description": "pg_trgm GIN index on public.thoughts.content to accelerate search_thoughts_text ILIKE fallback by ~50x on rare-word queries.", + "category": "schemas", + "author": { + "name": "Alan Shurafa", + "github": "alanshurafa" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": [], + "tools": [] + }, + "tags": ["performance", "search", "pg_trgm", "indexing"], + "difficulty": "beginner", + "estimated_time": "5 minutes", + "created": "2026-04-18", + "updated": "2026-04-18" +} diff --git a/schemas/text-search-trgm/schema.sql b/schemas/text-search-trgm/schema.sql new file mode 100644 index 000000000..b4c0b492e --- /dev/null +++ b/schemas/text-search-trgm/schema.sql @@ -0,0 +1,39 @@ +-- Add pg_trgm trigram GIN index to accelerate search_thoughts_text ILIKE fallback. +-- +-- Context: search_thoughts_text (from schemas/enhanced-thoughts) has a tsvector +-- phase (fast) and an ILIKE '%...%' fallback. The ILIKE fallback triggers for +-- most real queries -- tsvector usually returns fewer hits than requested and +-- the function fills in from ILIKE. Leading-wildcard ILIKE can't use the +-- tsvector GIN index, so without a trigram index ILIKE seq-scans the whole +-- thoughts table. On an 89K-row brain, that's 7-8s per rare-word query. +-- +-- Fix: pg_trgm provides trigram-based indexing that GIN can use for ILIKE +-- patterns. No changes to search_thoughts_text needed -- the Postgres planner +-- picks up the new index automatically once it exists. Rare-word queries drop +-- from ~8s to ~100-150ms. +-- +-- Prerequisites: enhanced-thoughts schema (PR #191) must be installed first. +-- This migration adds only the trigram index; tsvector index lives in +-- enhanced-thoughts. +-- +-- Tradeoffs: +-- - Storage: ~20-40MB on a 90K-thought brain; scales linearly with content size. +-- - Build lock: regular (non-CONCURRENT) CREATE INDEX briefly locks the +-- thoughts table against writes during the build (~1-2 min at 90K rows). +-- Switch to CREATE INDEX CONCURRENTLY if you're running live capture and +-- can tolerate migration-outside-transaction semantics. +-- - Write-amp: small INSERT/UPDATE overhead on content changes. Imperceptible +-- at typical personal-brain write rates. + +BEGIN; + +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +CREATE INDEX IF NOT EXISTS idx_thoughts_content_trgm + ON public.thoughts + USING gin (content gin_trgm_ops); + +COMMENT ON INDEX public.idx_thoughts_content_trgm IS + 'Trigram GIN index on content for ILIKE ''%foo%'' patterns. Accelerates search_thoughts_text ILIKE fallback from ~8s to ~150ms on rare-word queries.'; + +COMMIT; From f4cdb1a70cacdc950dbeb4f3abf34db29525be09 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:47:03 -0400 Subject: [PATCH 028/125] [recipes] Rewrite ob-graph recursive CTEs as iterative BFS (find_shortest_path + traverse_graph) The previous recursive-CTE implementations enumerated every path to every reachable node, which exploded on densely connected graphs (a hub with 1k+ neighbours at depth 2 produced tens of thousands of rows) and depended on the per-path ANY(path) check to break cycles. On a cyclic graph that exceeded the statement_timeout the planner never actually pruned the walk. Replace both functions with iterative plpgsql BFS: - Global seen-set (UUID[]) so each node is visited at most once - JSONB parent-pointer map records the first (parent, relation) that reached each node; BFS's "first discovery wins" invariant is enforced with DISTINCT ON (next_id) - Shared reconstruct_bfs_path() helper walks the parent map end -> start with a safety guard against malformed maps - find_shortest_path keeps bidirectional edge traversal; traverse_graph keeps outgoing-only with optional relationship_type filter - Signatures, argument order, RETURNS shape, and language (plpgsql, no SECURITY DEFINER) match the original so this is a body-only rewrite - All queries scope by p_user_id to preserve the multi-tenant isolation the edge function relies on (service_role bypasses RLS) Update the README's "How It Works" section and function table to describe the new implementation so the docs don't contradict the SQL. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/ob-graph/README.md | 47 ++--- recipes/ob-graph/schema.sql | 347 +++++++++++++++++++++++++++++------- 2 files changed, 305 insertions(+), 89 deletions(-) diff --git a/recipes/ob-graph/README.md b/recipes/ob-graph/README.md index 729e8dc07..693b30cf1 100644 --- a/recipes/ob-graph/README.md +++ b/recipes/ob-graph/README.md @@ -14,7 +14,7 @@ Adds two tables (`graph_nodes`, `graph_edges`) and an MCP server with 10 tools f - **Querying** relationships — get direct neighbors, multi-hop traversal, and shortest-path between any two nodes - **Linking** to existing thoughts — nodes can optionally reference a thought via `thought_id` -All graph traversal runs in PostgreSQL using recursive CTEs — no external graph database needed. +All graph traversal runs in PostgreSQL using an iterative BFS with a per-call seen-set — no external graph database needed. ## Prerequisites @@ -57,8 +57,9 @@ The schema creates: |--------|---------| | `graph_nodes` | Entities in your knowledge graph | | `graph_edges` | Directed relationships between nodes | -| `traverse_graph()` | Recursive CTE function for multi-hop traversal | -| `find_shortest_path()` | BFS shortest path between two nodes | +| `traverse_graph()` | Iterative BFS with a seen-set for multi-hop traversal | +| `find_shortest_path()` | Iterative BFS shortest path between two nodes | +| `reconstruct_bfs_path()` | Helper that walks the BFS parent map into a UUID path | | RLS policies | User-scoped data isolation on both tables | | Indexes | Fast lookups by user, type, label, source/target | @@ -160,31 +161,35 @@ Done when: Your AI can create nodes, connect them with edges, and traverse the g ## How the Graph Traversal Works -OB-Graph uses PostgreSQL recursive CTEs instead of a dedicated graph database. Here's the pattern: +OB-Graph uses an iterative plpgsql BFS instead of a dedicated graph database or a recursive CTE. Each call maintains a frontier (the current BFS layer), a seen-set of every node visited so far, and a JSONB parent-pointer map used to reconstruct paths. Each node is visited at most once, which keeps traversal bounded even on dense graphs or graphs with cycles: ```sql -WITH RECURSIVE graph_walk AS ( - -- Base case: start node - SELECT id, label, 0 AS depth, ARRAY[id] AS path - FROM graph_nodes WHERE id = start_id - - UNION ALL - - -- Recursive case: follow edges, prevent cycles via path check - SELECT n.id, n.label, gw.depth + 1, gw.path || n.id - FROM graph_walk gw - JOIN graph_edges e ON e.source_node_id = gw.id - JOIN graph_nodes n ON n.id = e.target_node_id - WHERE gw.depth < max_depth - AND NOT n.id = ANY(gw.path) -- cycle prevention -) -SELECT * FROM graph_walk; +-- Pseudocode for the BFS used by traverse_graph and find_shortest_path +v_frontier := ARRAY[start]; +v_seen := ARRAY[start]; +v_parent_map := '{}'::jsonb; + +WHILE depth < max_depth AND frontier is non-empty LOOP + -- Collect unseen neighbours of the current frontier, remembering the + -- (parent, relation) that first reached each. DISTINCT ON enforces + -- BFS's "first discovery wins" rule. + next_ids, next_map := SELECT ... FROM graph_edges + WHERE source_node_id = ANY(v_frontier) + AND NOT target_node_id = ANY(v_seen); + + v_parent_map := v_parent_map || next_map; + v_seen := v_seen || next_ids; + v_frontier := next_ids; + depth := depth + 1; +END LOOP; + +-- Walk parent pointers back from target to start for path reconstruction. ``` This approach works well for knowledge graphs with thousands of nodes. It stays within Supabase's free tier limits (no extra services or extensions required) and gives you traversal, pathfinding, and neighbor queries — the core operations you need for relationship exploration. > [!NOTE] -> Recursive CTEs are bounded by `max_depth` and cycle prevention. For very large graphs (100k+ edges), consider adding a `weight` threshold filter to prune low-confidence edges during traversal. +> Traversal is bounded by `max_depth` and the seen-set. For very large graphs (100k+ edges), consider adding a `weight` threshold filter to prune low-confidence edges before calling the functions. ## Expected Outcome diff --git a/recipes/ob-graph/schema.sql b/recipes/ob-graph/schema.sql index e78266435..2bcc9c99e 100644 --- a/recipes/ob-graph/schema.sql +++ b/recipes/ob-graph/schema.sql @@ -1,7 +1,7 @@ -- OB-Graph: Knowledge Graph Layer for Open Brain -- Adds graph database functionality on top of PostgreSQL using a nodes + edges --- pattern with recursive CTEs for traversal. Integrates with the core thoughts --- table without modifying it. +-- pattern with iterative plpgsql BFS (with a per-call seen-set) for traversal. +-- Integrates with the core thoughts table without modifying it. -- ============================================================================ -- Table: graph_nodes @@ -95,8 +95,14 @@ CREATE TRIGGER update_graph_nodes_updated_at -- ============================================================================ -- Function: traverse_graph --- Walks the graph from a starting node up to max_depth hops, returning all --- reachable nodes and the paths taken. Uses a recursive CTE. +-- Walks the graph from a starting node up to max_depth hops (outgoing edges +-- only), returning every reachable node with the depth at which it was first +-- discovered and the relationship that was used to reach it. +-- +-- Implemented as an iterative plpgsql BFS with a per-user seen-set. Each node +-- is visited at most once, which keeps the function bounded on densely +-- connected graphs where a recursive CTE would enumerate every path to every +-- reachable node and hit statement_timeout. -- ============================================================================ CREATE OR REPLACE FUNCTION traverse_graph( p_user_id UUID, @@ -112,47 +118,164 @@ RETURNS TABLE ( path UUID[], via_relationship TEXT ) AS $$ +DECLARE + v_depth INT := 0; + v_frontier UUID[] := ARRAY[p_start_node_id]::UUID[]; + v_seen UUID[] := ARRAY[p_start_node_id]::UUID[]; + -- parent_map: { "": { "parent": "", "relation": "" } } + v_parent_map JSONB := '{}'::jsonb; + v_next_ids UUID[]; + v_next_map JSONB; + v_start_exists BOOLEAN; BEGIN - RETURN QUERY - WITH RECURSIVE graph_walk AS ( - -- Base case: the start node + -- Validate the start node exists and belongs to the caller. If not, + -- emit nothing (mirrors the original CTE behaviour, which just returned + -- an empty result set when the base case produced no rows). + SELECT EXISTS ( + SELECT 1 FROM graph_nodes + WHERE id = p_start_node_id AND user_id = p_user_id + ) INTO v_start_exists; + + IF NOT v_start_exists THEN + RETURN; + END IF; + + -- Iterative BFS: at each layer, collect all unseen outgoing neighbours of + -- the current frontier, record the first parent + relationship that + -- reached each, add them to the seen-set, then recurse on the new layer. + WHILE v_depth < p_max_depth + AND v_frontier IS NOT NULL + AND array_length(v_frontier, 1) IS NOT NULL LOOP + SELECT - n.id AS node_id, - n.label AS node_label, - n.node_type AS node_type, - 0 AS depth, - ARRAY[n.id] AS path, - NULL::TEXT AS via_relationship - FROM graph_nodes n - WHERE n.id = p_start_node_id - AND n.user_id = p_user_id + COALESCE(array_agg(next_id), ARRAY[]::UUID[]), + COALESCE( + jsonb_object_agg( + next_id::text, + jsonb_build_object('parent', parent_id, 'relation', relation_type) + ), + '{}'::jsonb + ) + INTO v_next_ids, v_next_map + FROM ( + -- DISTINCT ON keeps only the first (parent, relation) pair that + -- reached each new node in this layer, which mirrors BFS's + -- "first discovery wins" invariant. + SELECT DISTINCT ON (next_id) next_id, parent_id, relation_type + FROM ( + SELECT + e.target_node_id AS next_id, + e.source_node_id AS parent_id, + e.relationship_type AS relation_type + FROM graph_edges e + JOIN graph_nodes n ON n.id = e.target_node_id + AND n.user_id = p_user_id + WHERE e.user_id = p_user_id + AND e.source_node_id = ANY(v_frontier) + AND NOT (e.target_node_id = ANY(v_seen)) + AND (p_relationship_type IS NULL + OR e.relationship_type = p_relationship_type) + ) layer + ORDER BY next_id + ) dedup; + + IF v_next_ids IS NULL OR array_length(v_next_ids, 1) IS NULL THEN + EXIT; + END IF; - UNION ALL + v_parent_map := v_parent_map || v_next_map; + v_seen := v_seen || v_next_ids; + v_frontier := v_next_ids; + v_depth := v_depth + 1; + END LOOP; - -- Recursive case: follow outgoing edges + -- Emit one row per discovered node. The start node sits at depth 0 with + -- a single-element path and NULL via_relationship. Every other node + -- reconstructs its path by walking parent pointers back to the start. + RETURN QUERY + WITH walked AS ( + SELECT + seen_id AS nid, + ordinality - 1 AS seen_idx + FROM unnest(v_seen) WITH ORDINALITY AS s(seen_id, ordinality) + ), + reconstructed AS ( SELECT - n.id, - n.label, - n.node_type, - gw.depth + 1, - gw.path || n.id, - e.relationship_type - FROM graph_walk gw - JOIN graph_edges e ON e.source_node_id = gw.node_id AND e.user_id = p_user_id - JOIN graph_nodes n ON n.id = e.target_node_id AND n.user_id = p_user_id - WHERE gw.depth < p_max_depth - AND NOT n.id = ANY(gw.path) -- prevent cycles - AND (p_relationship_type IS NULL OR e.relationship_type = p_relationship_type) + w.nid, + w.seen_idx, + CASE + WHEN w.nid = p_start_node_id THEN ARRAY[w.nid]::UUID[] + ELSE reconstruct_bfs_path(v_parent_map, p_start_node_id, w.nid) + END AS node_path, + CASE + WHEN w.nid = p_start_node_id THEN NULL + ELSE v_parent_map -> (w.nid::text) ->> 'relation' + END AS via_relation_text + FROM walked w ) - SELECT * FROM graph_walk - ORDER BY graph_walk.depth, graph_walk.node_label; + SELECT + n.id AS node_id, + n.label AS node_label, + n.node_type AS node_type, + (COALESCE(array_length(r.node_path, 1), 1) - 1)::INT AS depth, + r.node_path AS path, + r.via_relation_text AS via_relationship + FROM reconstructed r + JOIN graph_nodes n ON n.id = r.nid AND n.user_id = p_user_id + ORDER BY (COALESCE(array_length(r.node_path, 1), 1) - 1), n.label; END; $$ LANGUAGE plpgsql; +-- ============================================================================ +-- Helper: reconstruct_bfs_path +-- Walks a JSONB parent pointer map (built by the BFS functions below) from +-- a target node back to the start, returning the full path as a UUID[]. +-- Kept as a separate SQL function so traverse_graph and find_shortest_path +-- share the same reconstruction logic. +-- ============================================================================ +CREATE OR REPLACE FUNCTION reconstruct_bfs_path( + p_parent_map JSONB, + p_start_node_id UUID, + p_target_node_id UUID +) +RETURNS UUID[] AS $$ +DECLARE + v_path UUID[] := ARRAY[p_target_node_id]::UUID[]; + v_current UUID := p_target_node_id; + v_entry JSONB; + v_guard INT := 0; +BEGIN + -- v_guard caps the walk at a sane length so a malformed parent_map + -- (shouldn't happen, but defence-in-depth) can't spin forever. + WHILE v_current IS NOT NULL AND v_current <> p_start_node_id AND v_guard < 10000 LOOP + v_entry := p_parent_map -> (v_current::text); + IF v_entry IS NULL THEN + RETURN NULL; + END IF; + v_current := (v_entry ->> 'parent')::UUID; + v_path := v_current || v_path; + v_guard := v_guard + 1; + END LOOP; + + IF v_current IS NULL OR v_current <> p_start_node_id THEN + RETURN NULL; + END IF; + + RETURN v_path; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + -- ============================================================================ -- Function: find_shortest_path --- BFS shortest path between two nodes. Returns the node IDs along the path --- and the relationship types traversed. +-- Shortest path between two nodes, following edges in either direction. +-- Returns one row per step along the path (starting at step 1) with the node +-- that was reached and the relationship used to get there. +-- +-- Implemented as an iterative plpgsql BFS with a global seen-set. Each node +-- is visited at most once, so the function stays bounded even when the graph +-- contains cycles (A → B → A) or hub nodes with thousands of neighbours — +-- both of which could cause the previous recursive-CTE version to blow past +-- statement_timeout or exhaust memory. -- ============================================================================ CREATE OR REPLACE FUNCTION find_shortest_path( p_user_id UUID, @@ -166,49 +289,137 @@ RETURNS TABLE ( node_label TEXT, via_relationship TEXT ) AS $$ +DECLARE + v_depth INT := 0; + v_frontier UUID[] := ARRAY[p_start_node_id]::UUID[]; + v_seen UUID[] := ARRAY[p_start_node_id]::UUID[]; + v_parent_map JSONB := '{}'::jsonb; + v_next_ids UUID[]; + v_next_map JSONB; + v_found BOOLEAN := false; + v_path UUID[]; + v_start_exists BOOLEAN; + v_end_exists BOOLEAN; BEGIN - RETURN QUERY - WITH RECURSIVE bfs AS ( - SELECT - n.id AS node_id, - n.label AS node_label, - 0 AS depth, - ARRAY[n.id] AS path, - ARRAY[NULL::TEXT] AS relationships + IF p_start_node_id IS NULL OR p_end_node_id IS NULL THEN + RETURN; + END IF; + + -- Validate both endpoints exist for this user. If either is missing or + -- belongs to someone else, RLS would have excluded it anyway; return an + -- empty result set rather than silently falling through the BFS. + SELECT EXISTS ( + SELECT 1 FROM graph_nodes + WHERE id = p_start_node_id AND user_id = p_user_id + ) INTO v_start_exists; + SELECT EXISTS ( + SELECT 1 FROM graph_nodes + WHERE id = p_end_node_id AND user_id = p_user_id + ) INTO v_end_exists; + IF NOT v_start_exists OR NOT v_end_exists THEN + RETURN; + END IF; + + -- Trivial case: start == end. The original recursive-CTE version would + -- have emitted the single start node; preserve that shape. + IF p_start_node_id = p_end_node_id THEN + RETURN QUERY + SELECT 1::INT AS step, + n.id AS node_id, + n.label AS node_label, + NULL::TEXT AS via_relationship FROM graph_nodes n WHERE n.id = p_start_node_id - AND n.user_id = p_user_id + AND n.user_id = p_user_id; + RETURN; + END IF; - UNION ALL + -- Iterative BFS. Edges are followed in either direction (bidirectional + -- pathfinding, matching the original CTE's semantics). For each newly + -- discovered node we record the parent + relationship that first reached + -- it; DISTINCT ON (next_id) ensures only the first discovery wins. + WHILE v_depth < p_max_depth + AND NOT v_found + AND v_frontier IS NOT NULL + AND array_length(v_frontier, 1) IS NOT NULL LOOP SELECT - n.id, - n.label, - b.depth + 1, - b.path || n.id, - b.relationships || e.relationship_type - FROM bfs b - JOIN graph_edges e ON (e.source_node_id = b.node_id OR e.target_node_id = b.node_id) AND e.user_id = p_user_id - JOIN graph_nodes n ON n.id = CASE WHEN e.source_node_id = b.node_id THEN e.target_node_id ELSE e.source_node_id END - AND n.user_id = p_user_id - WHERE b.depth < p_max_depth - AND NOT n.id = ANY(b.path) - ), - shortest AS ( - SELECT path, relationships - FROM bfs - WHERE node_id = p_end_node_id - ORDER BY depth - LIMIT 1 - ) + COALESCE(array_agg(next_id), ARRAY[]::UUID[]), + COALESCE( + jsonb_object_agg( + next_id::text, + jsonb_build_object('parent', parent_id, 'relation', relation_type) + ), + '{}'::jsonb + ) + INTO v_next_ids, v_next_map + FROM ( + SELECT DISTINCT ON (next_id) next_id, parent_id, relation_type + FROM ( + SELECT + e.target_node_id AS next_id, + e.source_node_id AS parent_id, + e.relationship_type AS relation_type + FROM graph_edges e + WHERE e.user_id = p_user_id + AND e.source_node_id = ANY(v_frontier) + AND NOT (e.target_node_id = ANY(v_seen)) + + UNION ALL + + SELECT + e.source_node_id AS next_id, + e.target_node_id AS parent_id, + e.relationship_type AS relation_type + FROM graph_edges e + WHERE e.user_id = p_user_id + AND e.target_node_id = ANY(v_frontier) + AND NOT (e.source_node_id = ANY(v_seen)) + ) layer + JOIN graph_nodes n ON n.id = layer.next_id + AND n.user_id = p_user_id + ORDER BY next_id + ) dedup; + + IF v_next_ids IS NULL OR array_length(v_next_ids, 1) IS NULL THEN + EXIT; + END IF; + + v_parent_map := v_parent_map || v_next_map; + v_seen := v_seen || v_next_ids; + + IF p_end_node_id = ANY(v_next_ids) THEN + v_found := true; + EXIT; + END IF; + + v_frontier := v_next_ids; + v_depth := v_depth + 1; + END LOOP; + + IF NOT v_found THEN + RETURN; + END IF; + + -- Reconstruct the path end → start by walking parent pointers, then emit + -- one row per step. Step 1 is the start node with NULL via_relationship; + -- each subsequent step carries the relationship used to arrive there. + v_path := reconstruct_bfs_path(v_parent_map, p_start_node_id, p_end_node_id); + IF v_path IS NULL THEN + RETURN; + END IF; + + RETURN QUERY SELECT - row_number() OVER (ORDER BY u.ordinality)::INT AS step, + u.ordinality::INT AS step, gn.id AS node_id, gn.label AS node_label, - s.relationships[ordinality] AS via_relationship - FROM shortest s, - unnest(s.path) WITH ORDINALITY AS u(nid, ordinality) - JOIN graph_nodes gn ON gn.id = u.nid; + CASE WHEN u.ordinality = 1 THEN NULL + ELSE v_parent_map -> (u.nid::text) ->> 'relation' + END AS via_relationship + FROM unnest(v_path) WITH ORDINALITY AS u(nid, ordinality) + JOIN graph_nodes gn ON gn.id = u.nid AND gn.user_id = p_user_id + ORDER BY u.ordinality; END; $$ LANGUAGE plpgsql; From b6d6c2f07e269550b983688123645f5dc555ce7c Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:47:17 -0400 Subject: [PATCH 029/125] [recipes] Add smoke test for ob-graph RPCs after BFS rewrite Standalone Node script that exercises traverse_graph and find_shortest_path against a live Supabase project with ob-graph installed. Picks an arbitrary edge, calls both RPCs at depth 1/2/6, and prints row counts + timings so maintainers can confirm the iterative-BFS rewrite stays inside the statement_timeout and returns sensible shapes before shipping. Reads SUPABASE_PROJECT_REF, SUPABASE_SERVICE_ROLE_KEY, and OB_GRAPH_USER_ID from recipes/ob-graph/.env.local so the service-role key never lands in repo history. Sets process.exitCode=1 on any RPC failure so CI or shell chaining picks up a non-zero exit. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/ob-graph/smoke-graph-rpcs.mjs | 144 ++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 recipes/ob-graph/smoke-graph-rpcs.mjs diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs new file mode 100644 index 000000000..85af0d1c6 --- /dev/null +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -0,0 +1,144 @@ +#!/usr/bin/env node +// Smoke test for the ob-graph traversal RPCs. +// +// After applying the iterative-BFS rewrite in schema.sql, run this against a +// Supabase project that has the ob-graph schema installed to confirm both RPCs +// return sensible shapes and complete inside normal statement_timeout bounds. +// +// Usage: +// 1. Copy .env.local.example to .env.local (or reuse the one from your +// ob-graph recipe setup) with these vars set: +// SUPABASE_PROJECT_REF — the xxxx part of xxxx.supabase.co +// SUPABASE_SERVICE_ROLE_KEY — service role key (used server-side only) +// OB_GRAPH_USER_ID — the UUID the graph belongs to (DEFAULT_USER_ID) +// 2. Seed the graph with at least one edge (create_node + create_edge via the +// MCP server, or insert directly in the Supabase table editor). +// 3. node recipes/ob-graph/smoke-graph-rpcs.mjs +// +// The script picks one arbitrary edge, then calls traverse_graph at depth 1/2 +// and find_shortest_path between the edge endpoints, printing timings and row +// counts. The service_role key never leaves your machine. + +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +function loadEnv() { + const envPath = path.join(__dirname, ".env.local"); + if (!fs.existsSync(envPath)) { + console.error(`missing ${envPath} — create one with SUPABASE_PROJECT_REF, SUPABASE_SERVICE_ROLE_KEY, OB_GRAPH_USER_ID`); + process.exit(1); + } + const env = {}; + for (const line of fs.readFileSync(envPath, "utf8").split(/\r?\n/)) { + const m = line.match(/^([A-Z0-9_]+)=(.*)$/); + if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, ""); + } + const required = ["SUPABASE_PROJECT_REF", "SUPABASE_SERVICE_ROLE_KEY", "OB_GRAPH_USER_ID"]; + for (const k of required) { + if (!env[k]) { + console.error(`missing ${k} in .env.local`); + process.exit(1); + } + } + return env; +} + +const env = loadEnv(); +const base = `https://${env.SUPABASE_PROJECT_REF}.supabase.co/rest/v1`; +const userId = env.OB_GRAPH_USER_ID; +const headers = { + "apikey": env.SUPABASE_SERVICE_ROLE_KEY, + "Authorization": `Bearer ${env.SUPABASE_SERVICE_ROLE_KEY}`, + "Content-Type": "application/json", +}; + +async function timed(label, fn) { + const t0 = Date.now(); + try { + const result = await fn(); + console.log(` ${label}: ${Date.now() - t0}ms — ${result}`); + } catch (e) { + console.log(` ${label}: ${Date.now() - t0}ms — ERROR ${e.message}`); + process.exitCode = 1; + } +} + +async function rpc(name, body) { + const res = await fetch(`${base}/rpc/${name}`, { + method: "POST", + headers, + body: JSON.stringify(body), + }); + const text = await res.text(); + if (!res.ok) throw new Error(`${res.status} ${text.slice(0, 200)}`); + return JSON.parse(text); +} + +// Pick an arbitrary edge to use as the probe pair. +const edgeRes = await fetch( + `${base}/graph_edges?select=source_node_id,target_node_id&user_id=eq.${userId}&limit=1`, + { headers }, +); +const edges = await edgeRes.json(); +if (!Array.isArray(edges) || edges.length === 0) { + console.error("no edges found for this user — seed the graph (create_node + create_edge) before running the smoke test"); + process.exit(1); +} +const startId = edges[0].source_node_id; +const endId = edges[0].target_node_id; +console.log(`smoke: user=${userId} start=${startId} end=${endId}`); + +console.log("traverse_graph:"); +await timed("depth=1", async () => { + const rows = await rpc("traverse_graph", { + p_user_id: userId, + p_start_node_id: startId, + p_max_depth: 1, + p_relationship_type: null, + }); + return `rows=${rows.length}`; +}); +await timed("depth=2", async () => { + const rows = await rpc("traverse_graph", { + p_user_id: userId, + p_start_node_id: startId, + p_max_depth: 2, + p_relationship_type: null, + }); + return `rows=${rows.length}`; +}); + +console.log("find_shortest_path:"); +await timed("direct neighbor", async () => { + const rows = await rpc("find_shortest_path", { + p_user_id: userId, + p_start_node_id: startId, + p_end_node_id: endId, + p_max_depth: 6, + }); + return `hops=${rows.length ? rows.length - 1 : "no_path"}`; +}); + +// Random distant pair: use the newest node as an "unlikely to be directly +// connected" endpoint. With the seen-set fix this should still complete fast +// even if the two nodes are actually unreachable within max_depth. +const farRes = await fetch( + `${base}/graph_nodes?select=id&user_id=eq.${userId}&order=created_at.desc&limit=1`, + { headers }, +); +const farBody = await farRes.json(); +const farId = farBody[0]?.id; +if (farId && farId !== startId) { + await timed(`random pair (${startId}→${farId})`, async () => { + const rows = await rpc("find_shortest_path", { + p_user_id: userId, + p_start_node_id: startId, + p_end_node_id: farId, + p_max_depth: 6, + }); + return `hops=${rows.length ? rows.length - 1 : "no_path"}`; + }); +} From 2a8c6340193fac07fbf559d069a048fead3365bd Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:56:20 -0400 Subject: [PATCH 030/125] [schemas] Fix CI Rule 13: convert broken enhanced-thoughts link to external PR URL --- schemas/text-search-trgm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/text-search-trgm/README.md b/schemas/text-search-trgm/README.md index 117671b8a..52caefc9e 100644 --- a/schemas/text-search-trgm/README.md +++ b/schemas/text-search-trgm/README.md @@ -109,4 +109,4 @@ Solution: Check for long-running transactions holding locks on `thoughts` (look ## References - [PostgreSQL `pg_trgm` documentation](https://www.postgresql.org/docs/current/pgtrgm.html) -- official reference for trigram matching and index operator classes -- [`schemas/enhanced-thoughts`](../enhanced-thoughts/README.md) -- defines `search_thoughts_text`, the consumer of this index +- [`schemas/enhanced-thoughts` (PR #191)](https://github.com/NateBJones-Projects/OB1/pull/191) -- defines `search_thoughts_text`, the consumer of this index From 46185112c3e2971e8093e9b9f67ca658ee498651 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:59:02 -0400 Subject: [PATCH 031/125] [recipes] Fix REVIEW-CODEX-P1: revert traverse_graph to original recursive CTE (preserve contract) --- recipes/ob-graph/schema.sql | 147 ++++++++++-------------------------- 1 file changed, 39 insertions(+), 108 deletions(-) diff --git a/recipes/ob-graph/schema.sql b/recipes/ob-graph/schema.sql index 2bcc9c99e..141b6db99 100644 --- a/recipes/ob-graph/schema.sql +++ b/recipes/ob-graph/schema.sql @@ -1,7 +1,9 @@ -- OB-Graph: Knowledge Graph Layer for Open Brain -- Adds graph database functionality on top of PostgreSQL using a nodes + edges --- pattern with iterative plpgsql BFS (with a per-call seen-set) for traversal. --- Integrates with the core thoughts table without modifying it. +-- pattern. traverse_graph uses a recursive CTE (enumerates acyclic paths); +-- find_shortest_path uses an iterative plpgsql BFS (one row per reachable +-- node along the shortest path). Integrates with the core thoughts table +-- without modifying it. -- ============================================================================ -- Table: graph_nodes @@ -95,14 +97,14 @@ CREATE TRIGGER update_graph_nodes_updated_at -- ============================================================================ -- Function: traverse_graph --- Walks the graph from a starting node up to max_depth hops (outgoing edges --- only), returning every reachable node with the depth at which it was first --- discovered and the relationship that was used to reach it. +-- Walks the graph from a starting node up to max_depth hops, returning all +-- reachable nodes and the paths taken. Uses a recursive CTE. -- --- Implemented as an iterative plpgsql BFS with a per-user seen-set. Each node --- is visited at most once, which keeps the function bounded on densely --- connected graphs where a recursive CTE would enumerate every path to every --- reachable node and hit statement_timeout. +-- Semantics: one row per acyclic path. If there are multiple paths to the +-- same node (e.g. A→B via two different relationship_types, or A→B→D and +-- A→C→D), each path is emitted as its own row. The per-path cycle check +-- (NOT n.id = ANY(gw.path)) prevents infinite recursion; bound execution +-- further with p_max_depth on dense graphs. -- ============================================================================ CREATE OR REPLACE FUNCTION traverse_graph( p_user_id UUID, @@ -118,111 +120,40 @@ RETURNS TABLE ( path UUID[], via_relationship TEXT ) AS $$ -DECLARE - v_depth INT := 0; - v_frontier UUID[] := ARRAY[p_start_node_id]::UUID[]; - v_seen UUID[] := ARRAY[p_start_node_id]::UUID[]; - -- parent_map: { "": { "parent": "", "relation": "" } } - v_parent_map JSONB := '{}'::jsonb; - v_next_ids UUID[]; - v_next_map JSONB; - v_start_exists BOOLEAN; BEGIN - -- Validate the start node exists and belongs to the caller. If not, - -- emit nothing (mirrors the original CTE behaviour, which just returned - -- an empty result set when the base case produced no rows). - SELECT EXISTS ( - SELECT 1 FROM graph_nodes - WHERE id = p_start_node_id AND user_id = p_user_id - ) INTO v_start_exists; - - IF NOT v_start_exists THEN - RETURN; - END IF; - - -- Iterative BFS: at each layer, collect all unseen outgoing neighbours of - -- the current frontier, record the first parent + relationship that - -- reached each, add them to the seen-set, then recurse on the new layer. - WHILE v_depth < p_max_depth - AND v_frontier IS NOT NULL - AND array_length(v_frontier, 1) IS NOT NULL LOOP - + RETURN QUERY + WITH RECURSIVE graph_walk AS ( + -- Base case: the start node SELECT - COALESCE(array_agg(next_id), ARRAY[]::UUID[]), - COALESCE( - jsonb_object_agg( - next_id::text, - jsonb_build_object('parent', parent_id, 'relation', relation_type) - ), - '{}'::jsonb - ) - INTO v_next_ids, v_next_map - FROM ( - -- DISTINCT ON keeps only the first (parent, relation) pair that - -- reached each new node in this layer, which mirrors BFS's - -- "first discovery wins" invariant. - SELECT DISTINCT ON (next_id) next_id, parent_id, relation_type - FROM ( - SELECT - e.target_node_id AS next_id, - e.source_node_id AS parent_id, - e.relationship_type AS relation_type - FROM graph_edges e - JOIN graph_nodes n ON n.id = e.target_node_id - AND n.user_id = p_user_id - WHERE e.user_id = p_user_id - AND e.source_node_id = ANY(v_frontier) - AND NOT (e.target_node_id = ANY(v_seen)) - AND (p_relationship_type IS NULL - OR e.relationship_type = p_relationship_type) - ) layer - ORDER BY next_id - ) dedup; - - IF v_next_ids IS NULL OR array_length(v_next_ids, 1) IS NULL THEN - EXIT; - END IF; + n.id AS node_id, + n.label AS node_label, + n.node_type AS node_type, + 0 AS depth, + ARRAY[n.id] AS path, + NULL::TEXT AS via_relationship + FROM graph_nodes n + WHERE n.id = p_start_node_id + AND n.user_id = p_user_id - v_parent_map := v_parent_map || v_next_map; - v_seen := v_seen || v_next_ids; - v_frontier := v_next_ids; - v_depth := v_depth + 1; - END LOOP; + UNION ALL - -- Emit one row per discovered node. The start node sits at depth 0 with - -- a single-element path and NULL via_relationship. Every other node - -- reconstructs its path by walking parent pointers back to the start. - RETURN QUERY - WITH walked AS ( - SELECT - seen_id AS nid, - ordinality - 1 AS seen_idx - FROM unnest(v_seen) WITH ORDINALITY AS s(seen_id, ordinality) - ), - reconstructed AS ( + -- Recursive case: follow outgoing edges SELECT - w.nid, - w.seen_idx, - CASE - WHEN w.nid = p_start_node_id THEN ARRAY[w.nid]::UUID[] - ELSE reconstruct_bfs_path(v_parent_map, p_start_node_id, w.nid) - END AS node_path, - CASE - WHEN w.nid = p_start_node_id THEN NULL - ELSE v_parent_map -> (w.nid::text) ->> 'relation' - END AS via_relation_text - FROM walked w + n.id, + n.label, + n.node_type, + gw.depth + 1, + gw.path || n.id, + e.relationship_type + FROM graph_walk gw + JOIN graph_edges e ON e.source_node_id = gw.node_id AND e.user_id = p_user_id + JOIN graph_nodes n ON n.id = e.target_node_id AND n.user_id = p_user_id + WHERE gw.depth < p_max_depth + AND NOT n.id = ANY(gw.path) -- prevent cycles + AND (p_relationship_type IS NULL OR e.relationship_type = p_relationship_type) ) - SELECT - n.id AS node_id, - n.label AS node_label, - n.node_type AS node_type, - (COALESCE(array_length(r.node_path, 1), 1) - 1)::INT AS depth, - r.node_path AS path, - r.via_relation_text AS via_relationship - FROM reconstructed r - JOIN graph_nodes n ON n.id = r.nid AND n.user_id = p_user_id - ORDER BY (COALESCE(array_length(r.node_path, 1), 1) - 1), n.label; + SELECT * FROM graph_walk + ORDER BY graph_walk.depth, graph_walk.node_label; END; $$ LANGUAGE plpgsql; From cd74dd30f656b71070131f696449f1b0f8b5298d Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 18:59:20 -0400 Subject: [PATCH 032/125] [recipes] Fix REVIEW-CODEX-P2: revoke reconstruct_bfs_path from anon/authenticated --- recipes/ob-graph/schema.sql | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/recipes/ob-graph/schema.sql b/recipes/ob-graph/schema.sql index 141b6db99..408cda57d 100644 --- a/recipes/ob-graph/schema.sql +++ b/recipes/ob-graph/schema.sql @@ -359,3 +359,12 @@ $$ LANGUAGE plpgsql; -- ============================================================================ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.graph_nodes TO service_role; GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.graph_edges TO service_role; + +-- ============================================================================ +-- Lock down internal helper +-- reconstruct_bfs_path is plumbing for find_shortest_path (and any future +-- BFS helper). It should not be exposed as a PostgREST RPC to anon or +-- authenticated roles — only server-side callers (service_role) need it. +-- ============================================================================ +REVOKE ALL ON FUNCTION public.reconstruct_bfs_path(jsonb, uuid, uuid) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.reconstruct_bfs_path(jsonb, uuid, uuid) TO service_role; From 4affcef54890de09e8c25e8088cd8ba0376ca522 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:01:22 -0400 Subject: [PATCH 033/125] [recipes] Fix REVIEW-CODEX-P3: docs and env-example naming consistency --- recipes/ob-graph/README.md | 18 +++++++++++------- recipes/ob-graph/index.ts | 4 +++- recipes/ob-graph/metadata.json | 4 ++-- recipes/ob-graph/smoke-graph-rpcs.mjs | 22 +++++++++++----------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/recipes/ob-graph/README.md b/recipes/ob-graph/README.md index 693b30cf1..b4b60e06b 100644 --- a/recipes/ob-graph/README.md +++ b/recipes/ob-graph/README.md @@ -14,7 +14,7 @@ Adds two tables (`graph_nodes`, `graph_edges`) and an MCP server with 10 tools f - **Querying** relationships — get direct neighbors, multi-hop traversal, and shortest-path between any two nodes - **Linking** to existing thoughts — nodes can optionally reference a thought via `thought_id` -All graph traversal runs in PostgreSQL using an iterative BFS with a per-call seen-set — no external graph database needed. +All graph traversal runs in PostgreSQL — `traverse_graph` uses a recursive CTE (enumerating acyclic paths) and `find_shortest_path` uses an iterative BFS with a seen-set. No external graph database needed. ## Prerequisites @@ -57,9 +57,9 @@ The schema creates: |--------|---------| | `graph_nodes` | Entities in your knowledge graph | | `graph_edges` | Directed relationships between nodes | -| `traverse_graph()` | Iterative BFS with a seen-set for multi-hop traversal | +| `traverse_graph()` | Recursive CTE for multi-hop traversal (one row per acyclic path) | | `find_shortest_path()` | Iterative BFS shortest path between two nodes | -| `reconstruct_bfs_path()` | Helper that walks the BFS parent map into a UUID path | +| `reconstruct_bfs_path()` | Internal helper that walks the BFS parent map (service_role only) | | RLS policies | User-scoped data isolation on both tables | | Indexes | Fast lookups by user, type, label, source/target | @@ -161,10 +161,14 @@ Done when: Your AI can create nodes, connect them with edges, and traverse the g ## How the Graph Traversal Works -OB-Graph uses an iterative plpgsql BFS instead of a dedicated graph database or a recursive CTE. Each call maintains a frontier (the current BFS layer), a seen-set of every node visited so far, and a JSONB parent-pointer map used to reconstruct paths. Each node is visited at most once, which keeps traversal bounded even on dense graphs or graphs with cycles: +OB-Graph uses two different traversal strategies, each matched to what the operation actually needs: + +**`traverse_graph` — recursive CTE (one row per acyclic path).** The recursive CTE enumerates every acyclic path from the start node up to `p_max_depth` hops. Parallel edges and alternate routes are preserved as separate rows — if `A → B` exists with two different `relationship_type`s, you get two rows for B. A per-path cycle check (`NOT n.id = ANY(gw.path)`) prevents infinite recursion; `p_max_depth` bounds the search on dense graphs. + +**`find_shortest_path` — iterative plpgsql BFS with a seen-set.** Shortest path has a "one result per reachable node" semantics by definition, so a BFS with a global seen-set is the right fit. Each call maintains a frontier (the current BFS layer), a seen-set of every node visited so far, and a JSONB parent-pointer map used to reconstruct the path. Each node is visited at most once, which keeps pathfinding bounded even on graphs with cycles or hub nodes. ```sql --- Pseudocode for the BFS used by traverse_graph and find_shortest_path +-- Pseudocode for the BFS used by find_shortest_path v_frontier := ARRAY[start]; v_seen := ARRAY[start]; v_parent_map := '{}'::jsonb; @@ -186,10 +190,10 @@ END LOOP; -- Walk parent pointers back from target to start for path reconstruction. ``` -This approach works well for knowledge graphs with thousands of nodes. It stays within Supabase's free tier limits (no extra services or extensions required) and gives you traversal, pathfinding, and neighbor queries — the core operations you need for relationship exploration. +This split works well for knowledge graphs with thousands of nodes. It stays within Supabase's free tier limits (no extra services or extensions required) and gives you traversal, pathfinding, and neighbor queries — the core operations you need for relationship exploration. > [!NOTE] -> Traversal is bounded by `max_depth` and the seen-set. For very large graphs (100k+ edges), consider adding a `weight` threshold filter to prune low-confidence edges before calling the functions. +> `traverse_graph` can be expensive on dense graphs because it enumerates every acyclic path. Keep `p_max_depth` modest (2–3 is usually enough for exploration), or narrow with `p_relationship_type`. `find_shortest_path` is bounded by the seen-set; for very large graphs (100k+ edges), consider adding a `weight` threshold filter to prune low-confidence edges before calling the functions. ## Expected Outcome diff --git a/recipes/ob-graph/index.ts b/recipes/ob-graph/index.ts index af0c75c65..8323441ab 100644 --- a/recipes/ob-graph/index.ts +++ b/recipes/ob-graph/index.ts @@ -2,7 +2,9 @@ * OB-Graph: Knowledge Graph MCP Server for Open Brain * * Provides tools for building and querying a knowledge graph on top of - * PostgreSQL. Uses a nodes + edges model with recursive CTEs for traversal. + * PostgreSQL. Uses a nodes + edges model: traverse_graph is a recursive CTE + * (enumerates acyclic paths); find_shortest_path is an iterative plpgsql BFS + * with a seen-set. * * Tools: * - create_node — Add a node to the graph diff --git a/recipes/ob-graph/metadata.json b/recipes/ob-graph/metadata.json index a94f833da..68c97e649 100644 --- a/recipes/ob-graph/metadata.json +++ b/recipes/ob-graph/metadata.json @@ -1,6 +1,6 @@ { "name": "ob-graph", - "description": "A knowledge graph layer for Open Brain. Adds graph database functionality using PostgreSQL nodes + edges with recursive CTE traversal, shortest-path queries, and a full MCP server for building and exploring relationship networks across your thoughts and entities.", + "description": "A knowledge graph layer for Open Brain. Adds graph database functionality using PostgreSQL nodes + edges with recursive-CTE traversal, iterative-BFS shortest-path queries, and a full MCP server for building and exploring relationship networks across your thoughts and entities.", "category": "recipes", "author": { "name": "Nate Jones", @@ -26,5 +26,5 @@ "difficulty": "intermediate", "estimated_time": "30 minutes", "created": "2026-04-05", - "updated": "2026-04-05" + "updated": "2026-04-17" } diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs index 85af0d1c6..879aad45a 100644 --- a/recipes/ob-graph/smoke-graph-rpcs.mjs +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -1,16 +1,16 @@ #!/usr/bin/env node // Smoke test for the ob-graph traversal RPCs. // -// After applying the iterative-BFS rewrite in schema.sql, run this against a -// Supabase project that has the ob-graph schema installed to confirm both RPCs -// return sensible shapes and complete inside normal statement_timeout bounds. +// Run this against a Supabase project that has the ob-graph schema installed +// to confirm both RPCs (traverse_graph + find_shortest_path) return sensible +// shapes and complete inside normal statement_timeout bounds. // // Usage: -// 1. Copy .env.local.example to .env.local (or reuse the one from your -// ob-graph recipe setup) with these vars set: -// SUPABASE_PROJECT_REF — the xxxx part of xxxx.supabase.co +// 1. Copy .env.example to .env.local (same file, same variables — this +// script just reads .env.local so your real secrets stay gitignored): +// SUPABASE_URL — https://YOUR_PROJECT_REF.supabase.co // SUPABASE_SERVICE_ROLE_KEY — service role key (used server-side only) -// OB_GRAPH_USER_ID — the UUID the graph belongs to (DEFAULT_USER_ID) +// DEFAULT_USER_ID — the UUID the graph belongs to // 2. Seed the graph with at least one edge (create_node + create_edge via the // MCP server, or insert directly in the Supabase table editor). // 3. node recipes/ob-graph/smoke-graph-rpcs.mjs @@ -28,7 +28,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); function loadEnv() { const envPath = path.join(__dirname, ".env.local"); if (!fs.existsSync(envPath)) { - console.error(`missing ${envPath} — create one with SUPABASE_PROJECT_REF, SUPABASE_SERVICE_ROLE_KEY, OB_GRAPH_USER_ID`); + console.error(`missing ${envPath} — copy .env.example to .env.local and fill in SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, DEFAULT_USER_ID`); process.exit(1); } const env = {}; @@ -36,7 +36,7 @@ function loadEnv() { const m = line.match(/^([A-Z0-9_]+)=(.*)$/); if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, ""); } - const required = ["SUPABASE_PROJECT_REF", "SUPABASE_SERVICE_ROLE_KEY", "OB_GRAPH_USER_ID"]; + const required = ["SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY", "DEFAULT_USER_ID"]; for (const k of required) { if (!env[k]) { console.error(`missing ${k} in .env.local`); @@ -47,8 +47,8 @@ function loadEnv() { } const env = loadEnv(); -const base = `https://${env.SUPABASE_PROJECT_REF}.supabase.co/rest/v1`; -const userId = env.OB_GRAPH_USER_ID; +const base = `${env.SUPABASE_URL.replace(/\/+$/, "")}/rest/v1`; +const userId = env.DEFAULT_USER_ID; const headers = { "apikey": env.SUPABASE_SERVICE_ROLE_KEY, "Authorization": `Bearer ${env.SUPABASE_SERVICE_ROLE_KEY}`, From 9353eef3f59e2ac3478bcf02d33e6d334a249166 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:02:15 -0400 Subject: [PATCH 034/125] [recipes] Add cyclic and multi-path smoke-test cases to catch regressions --- recipes/ob-graph/smoke-graph-rpcs.mjs | 138 ++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs index 879aad45a..e0e011813 100644 --- a/recipes/ob-graph/smoke-graph-rpcs.mjs +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -142,3 +142,141 @@ if (farId && farId !== startId) { return `hops=${rows.length ? rows.length - 1 : "no_path"}`; }); } + +// --------------------------------------------------------------------------- +// Scenario tests — seed + assert + clean up. These catch regressions in +// traverse_graph semantics (multi-path enumeration, parallel relationship +// types) and in BFS cycle-safety. Each scenario namespaces its nodes by +// label prefix so reruns are idempotent and a failed run is easy to clean up +// manually from the Supabase Table Editor (filter by label LIKE 'smoke:%'). +// --------------------------------------------------------------------------- + +async function tableInsert(table, rows) { + const res = await fetch(`${base}/${table}`, { + method: "POST", + headers: { ...headers, "Prefer": "return=representation" }, + body: JSON.stringify(rows), + }); + const text = await res.text(); + if (!res.ok) throw new Error(`${res.status} ${text.slice(0, 200)}`); + return JSON.parse(text); +} + +async function tableDelete(table, filter) { + const res = await fetch(`${base}/${table}?${filter}`, { + method: "DELETE", + headers, + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`${res.status} ${text.slice(0, 200)}`); + } +} + +async function cleanupScenario(tag) { + // graph_edges has ON DELETE CASCADE from graph_nodes, so deleting the + // tagged nodes is enough to also drop their edges. + await tableDelete( + "graph_nodes", + `user_id=eq.${userId}&label=like.smoke:${tag}:%`, + ); +} + +function assert(cond, msg) { + if (!cond) { + console.log(` FAIL: ${msg}`); + process.exitCode = 1; + } else { + console.log(` ok: ${msg}`); + } +} + +// Scenario 1 — multi-path DAG. A → B → D and A → C → D. traverse_graph +// should return D twice (once via B, once via C). Also A→B has two parallel +// edges with different relationship_types, so B should appear twice at depth 1. +async function scenarioMultiPath() { + const tag = "multipath"; + await cleanupScenario(tag); + console.log(`scenario: multi-path DAG (tag=smoke:${tag}:*)`); + try { + const [a, b, c, d] = await tableInsert("graph_nodes", [ + { user_id: userId, label: `smoke:${tag}:A`, node_type: "concept" }, + { user_id: userId, label: `smoke:${tag}:B`, node_type: "concept" }, + { user_id: userId, label: `smoke:${tag}:C`, node_type: "concept" }, + { user_id: userId, label: `smoke:${tag}:D`, node_type: "concept" }, + ]); + await tableInsert("graph_edges", [ + { user_id: userId, source_node_id: a.id, target_node_id: b.id, relationship_type: "knows" }, + { user_id: userId, source_node_id: a.id, target_node_id: b.id, relationship_type: "likes" }, + { user_id: userId, source_node_id: a.id, target_node_id: c.id, relationship_type: "knows" }, + { user_id: userId, source_node_id: b.id, target_node_id: d.id, relationship_type: "knows" }, + { user_id: userId, source_node_id: c.id, target_node_id: d.id, relationship_type: "knows" }, + ]); + + const rows = await rpc("traverse_graph", { + p_user_id: userId, + p_start_node_id: a.id, + p_max_depth: 2, + p_relationship_type: null, + }); + + const bRows = rows.filter((r) => r.node_id === b.id); + const dRows = rows.filter((r) => r.node_id === d.id); + // A→B via knows and A→B via likes should both appear at depth 1. + assert(bRows.length === 2, `B reached twice at depth 1 via parallel edges (got ${bRows.length})`); + // A→B→D and A→C→D should both appear at depth 2. + assert(dRows.length === 2, `D reached twice at depth 2 via two distinct paths (got ${dRows.length})`); + assert(dRows.every((r) => r.depth === 2), `both D rows at depth 2 (got depths ${dRows.map((r) => r.depth).join(",")})`); + } finally { + await cleanupScenario(tag); + } +} + +// Scenario 2 — cycle. A → B → C → A. find_shortest_path must terminate and +// not loop; traverse_graph must also terminate (per-path cycle check). +async function scenarioCycle() { + const tag = "cycle"; + await cleanupScenario(tag); + console.log(`scenario: cycle A→B→C→A (tag=smoke:${tag}:*)`); + try { + const [a, b, c] = await tableInsert("graph_nodes", [ + { user_id: userId, label: `smoke:${tag}:A`, node_type: "concept" }, + { user_id: userId, label: `smoke:${tag}:B`, node_type: "concept" }, + { user_id: userId, label: `smoke:${tag}:C`, node_type: "concept" }, + ]); + await tableInsert("graph_edges", [ + { user_id: userId, source_node_id: a.id, target_node_id: b.id, relationship_type: "knows" }, + { user_id: userId, source_node_id: b.id, target_node_id: c.id, relationship_type: "knows" }, + { user_id: userId, source_node_id: c.id, target_node_id: a.id, relationship_type: "knows" }, + ]); + + const t0 = Date.now(); + const traverseRows = await rpc("traverse_graph", { + p_user_id: userId, + p_start_node_id: a.id, + p_max_depth: 5, + p_relationship_type: null, + }); + const traverseMs = Date.now() - t0; + assert(traverseMs < 2000, `traverse_graph on cycle terminated quickly (${traverseMs}ms)`); + assert(traverseRows.length > 0, `traverse_graph on cycle returned rows (${traverseRows.length})`); + + const t1 = Date.now(); + const pathRows = await rpc("find_shortest_path", { + p_user_id: userId, + p_start_node_id: a.id, + p_end_node_id: c.id, + p_max_depth: 6, + }); + const pathMs = Date.now() - t1; + assert(pathMs < 2000, `find_shortest_path on cycle terminated quickly (${pathMs}ms)`); + assert(pathRows.length === 3, `shortest A→C is 2 hops (3 steps incl. start) — got ${pathRows.length}`); + } finally { + await cleanupScenario(tag); + } +} + +console.log(""); +await scenarioMultiPath(); +console.log(""); +await scenarioCycle(); From 1e26c33451d072c9eb621a3964368edc3de3d352 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:11:08 -0400 Subject: [PATCH 035/125] [recipes] Fix REVIEW-CODEX-2-P1: smoke-test cycle assertion honors bidirectional shortest-path --- recipes/ob-graph/smoke-graph-rpcs.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs index e0e011813..843c70f45 100644 --- a/recipes/ob-graph/smoke-graph-rpcs.mjs +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -270,7 +270,18 @@ async function scenarioCycle() { }); const pathMs = Date.now() - t1; assert(pathMs < 2000, `find_shortest_path on cycle terminated quickly (${pathMs}ms)`); - assert(pathRows.length === 3, `shortest A→C is 2 hops (3 steps incl. start) — got ${pathRows.length}`); + // find_shortest_path is bidirectional (traverses edges regardless of + // direction). With the reverse edge C→A present, A reaches C in 1 hop, so + // the correct result is 2 rows (start + target). Assert shape, not a + // fixed count, so the test survives future traversal changes. + assert( + Array.isArray(pathRows) && pathRows.length >= 2, + `shortest path returned at least 2 rows (start + target) — got ${pathRows?.length}`, + ); + const firstId = pathRows[0]?.node_id ?? pathRows[0]?.id; + const lastId = pathRows[pathRows.length - 1]?.node_id ?? pathRows[pathRows.length - 1]?.id; + assert(firstId === a.id, `path starts at A (${a.id}) — got ${firstId}`); + assert(lastId === c.id, `path ends at C (${c.id}) — got ${lastId}`); } finally { await cleanupScenario(tag); } From cd11bf0bc9cf608522a3dd5d933cf65e49fdb33d Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:17:47 -0400 Subject: [PATCH 036/125] [recipes] Fix REVIEW-CODEX-3-P1: tighten cycle assertion to exact length === 2 to protect bidirectional shortest-path --- recipes/ob-graph/smoke-graph-rpcs.mjs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs index 843c70f45..4522a3a35 100644 --- a/recipes/ob-graph/smoke-graph-rpcs.mjs +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -272,11 +272,12 @@ async function scenarioCycle() { assert(pathMs < 2000, `find_shortest_path on cycle terminated quickly (${pathMs}ms)`); // find_shortest_path is bidirectional (traverses edges regardless of // direction). With the reverse edge C→A present, A reaches C in 1 hop, so - // the correct result is 2 rows (start + target). Assert shape, not a - // fixed count, so the test survives future traversal changes. + // the correct result is exactly 2 rows (start + target). Assert the exact + // length — a regression back to the 3-row A→B→C path would pass `>= 2` + // and silently erode the bidirectional-shortest-path guarantee. assert( - Array.isArray(pathRows) && pathRows.length >= 2, - `shortest path returned at least 2 rows (start + target) — got ${pathRows?.length}`, + Array.isArray(pathRows) && pathRows.length === 2, + `bidirectional shortest path returns exactly 2 rows (A,C via C→A reverse) — got ${pathRows?.length}`, ); const firstId = pathRows[0]?.node_id ?? pathRows[0]?.id; const lastId = pathRows[pathRows.length - 1]?.node_id ?? pathRows[pathRows.length - 1]?.id; From d5d24d654a820a5ab126fb6c4fe2bf99edfa3460 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:35:54 -0400 Subject: [PATCH 037/125] [schemas] Fix REVIEW-CLAUDE-MEDIUM-1: ANALYZE as explicit post-migration step --- schemas/text-search-trgm/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/schemas/text-search-trgm/README.md b/schemas/text-search-trgm/README.md index 52caefc9e..4771819a8 100644 --- a/schemas/text-search-trgm/README.md +++ b/schemas/text-search-trgm/README.md @@ -25,6 +25,8 @@ Installs the `pg_trgm` extension and creates a trigram GIN index on `public.thou - [`schemas/enhanced-thoughts`](https://github.com/NateBJones-Projects/OB1/pull/191) installed (defines `search_thoughts_text` and the base tsvector index) - Supabase project with write access to run migrations +This migration installs without error on stock OB1 (without PR #191), but provides no measurable benefit unless `search_thoughts_text` is installed. Install PR #191 first for the full effect. + ## Credential Tracker Copy this block into a text editor and fill it in as you go. @@ -45,8 +47,9 @@ SUPABASE (from your Open Brain setup) 1. Open your Supabase dashboard and navigate to the **SQL Editor** 2. Create a new query and paste the full contents of `schema.sql` 3. Click **Run** to execute the migration (the `CREATE INDEX` will briefly lock the `thoughts` table against writes; ~1-2 minutes at 90K rows) -4. Navigate to **Database > Extensions** and confirm `pg_trgm` is enabled -5. Navigate to **Database > Indexes** (or run the verification query below) and confirm `idx_thoughts_content_trgm` exists on `public.thoughts` +4. In a new query, run `ANALYZE public.thoughts;` to refresh planner statistics so the new index is picked up immediately. `ANALYZE` cannot run inside the migration's transaction, so it must be a separate command. +5. Navigate to **Database > Extensions** and confirm `pg_trgm` is enabled +6. Navigate to **Database > Indexes** (or run the verification query below) and confirm `idx_thoughts_content_trgm` exists on `public.thoughts` ## Expected Outcome @@ -54,7 +57,7 @@ After running the migration: - The `pg_trgm` extension is installed in the database. - A GIN trigram index named `idx_thoughts_content_trgm` exists on `public.thoughts(content)`. -- The next `search_thoughts_text` call whose ILIKE fallback fires will complete in ~100-150ms instead of ~8s. +- With `ANALYZE public.thoughts;` run (Step 4), the next `search_thoughts_text` call whose ILIKE fallback fires completes in ~100-150ms instead of ~8s. ## Verification @@ -101,7 +104,7 @@ The `pg_trgm` extension is left installed; it is harmless on its own and may be Solution: Your Supabase project predates automatic extension availability. In the SQL Editor, run `CREATE EXTENSION pg_trgm;` as a superuser or contact Supabase support. The migration uses `CREATE EXTENSION IF NOT EXISTS`, which works on all current Supabase projects. **Issue: `EXPLAIN ANALYZE` still shows `Seq Scan on thoughts`** -Solution: Run `ANALYZE public.thoughts;` to refresh planner statistics, then retry. The planner needs accurate row counts before it will choose an index scan over a seq scan on small tables. +Solution: If you somehow skipped Step 4, run `ANALYZE public.thoughts;` to refresh planner statistics, then retry. The planner needs accurate row counts before it will choose an index scan over a seq scan on small tables. **Issue: Migration hangs on `CREATE INDEX`** Solution: Check for long-running transactions holding locks on `thoughts` (look at `pg_stat_activity`). The index build needs to acquire a `SHARE` lock on the table. If you can't stop the blocking transaction, switch to `CREATE INDEX CONCURRENTLY` (see Tradeoffs). From beb4818a7ec33f7a310f0e80090ec7fc62a244cd Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:36:25 -0400 Subject: [PATCH 038/125] =?UTF-8?q?[recipes]=20Fix=20REVIEW-CLAUDE-WR-01:?= =?UTF-8?q?=20scenarioMultiPath=20arithmetic=20=E2=80=94=20parallel=20edge?= =?UTF-8?q?s=20produce=203=20D-rows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recipes/ob-graph/smoke-graph-rpcs.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/recipes/ob-graph/smoke-graph-rpcs.mjs b/recipes/ob-graph/smoke-graph-rpcs.mjs index 4522a3a35..0433c6ab2 100644 --- a/recipes/ob-graph/smoke-graph-rpcs.mjs +++ b/recipes/ob-graph/smoke-graph-rpcs.mjs @@ -224,8 +224,9 @@ async function scenarioMultiPath() { const dRows = rows.filter((r) => r.node_id === d.id); // A→B via knows and A→B via likes should both appear at depth 1. assert(bRows.length === 2, `B reached twice at depth 1 via parallel edges (got ${bRows.length})`); - // A→B→D and A→C→D should both appear at depth 2. - assert(dRows.length === 2, `D reached twice at depth 2 via two distinct paths (got ${dRows.length})`); + // Each of the 2 B-rows joins the single B→D edge (2 D-rows via B), plus 1 D-row via C. + assert(dRows.length === 3, + `D reached 3 times (2 via B parallel edges → D, 1 via C → D) — got ${dRows.length}`); assert(dRows.every((r) => r.depth === 2), `both D rows at depth 2 (got depths ${dRows.map((r) => r.depth).join(",")})`); } finally { await cleanupScenario(tag); From c3c8867fc853542cfae6ffe264788774d9e4703f Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:36:35 -0400 Subject: [PATCH 039/125] [recipes] Fix REVIEW-CLAUDE-WR-02: service_role-only execute on find_shortest_path + traverse_graph --- recipes/ob-graph/schema.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/recipes/ob-graph/schema.sql b/recipes/ob-graph/schema.sql index 408cda57d..7cad48ed3 100644 --- a/recipes/ob-graph/schema.sql +++ b/recipes/ob-graph/schema.sql @@ -368,3 +368,16 @@ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.graph_edges TO service_role -- ============================================================================ REVOKE ALL ON FUNCTION public.reconstruct_bfs_path(jsonb, uuid, uuid) FROM PUBLIC, anon, authenticated; GRANT EXECUTE ON FUNCTION public.reconstruct_bfs_path(jsonb, uuid, uuid) TO service_role; + +-- ============================================================================ +-- Lock down RPC entry points +-- find_shortest_path and traverse_graph are intended for server-side callers +-- (edge functions using SUPABASE_SERVICE_ROLE_KEY). Revoke default PUBLIC +-- EXECUTE so PostgREST rejects anon/authenticated calls at the entry point +-- rather than crashing inside reconstruct_bfs_path on a permission error. +-- ============================================================================ +REVOKE ALL ON FUNCTION public.find_shortest_path(uuid, uuid, uuid, int) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.find_shortest_path(uuid, uuid, uuid, int) TO service_role; + +REVOKE ALL ON FUNCTION public.traverse_graph(uuid, uuid, int, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.traverse_graph(uuid, uuid, int, text) TO service_role; From 3c925a1f3b6c31e6594414cb865d4b652b7e88a4 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:36:45 -0400 Subject: [PATCH 040/125] =?UTF-8?q?[recipes]=20Fix=20REVIEW-CLAUDE-IN-01:?= =?UTF-8?q?=20version=20bump=201.0.0=20=E2=86=92=201.0.1=20for=20bug-fix?= =?UTF-8?q?=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recipes/ob-graph/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes/ob-graph/metadata.json b/recipes/ob-graph/metadata.json index 68c97e649..4dc492835 100644 --- a/recipes/ob-graph/metadata.json +++ b/recipes/ob-graph/metadata.json @@ -6,7 +6,7 @@ "name": "Nate Jones", "github": "NateBJones-Projects" }, - "version": "1.0.0", + "version": "1.0.1", "requires": { "open_brain": true, "services": ["Supabase"], @@ -26,5 +26,5 @@ "difficulty": "intermediate", "estimated_time": "30 minutes", "created": "2026-04-05", - "updated": "2026-04-17" + "updated": "2026-04-18" } From 1db2770f119eb90355d133fc47f77d0ae13493b5 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 19:47:21 -0400 Subject: [PATCH 041/125] [recipes] Fix REVIEW-CODEX-5-P3: runtime version strings in index.ts match metadata 1.0.1 --- recipes/ob-graph/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes/ob-graph/index.ts b/recipes/ob-graph/index.ts index 8323441ab..ec8ccb02b 100644 --- a/recipes/ob-graph/index.ts +++ b/recipes/ob-graph/index.ts @@ -60,7 +60,7 @@ app.post("*", async (c) => { } const server = new McpServer( - { name: "ob-graph", version: "1.0.0" }, + { name: "ob-graph", version: "1.0.1" }, ); // ========================================================================== @@ -534,6 +534,6 @@ app.post("*", async (c) => { return transport.handleRequest(c); }); -app.get("*", (c) => c.json({ status: "ok", service: "OB-Graph MCP", version: "1.0.0" })); +app.get("*", (c) => c.json({ status: "ok", service: "OB-Graph MCP", version: "1.0.1" })); Deno.serve(app.fetch); From 133d9b95421f7a2cb77604d3286e0a9649c3848a Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 20:16:57 -0400 Subject: [PATCH 042/125] [recipes] Fix markdownlint MD031: blank lines around fenced code blocks in README --- recipes/ob-graph/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recipes/ob-graph/README.md b/recipes/ob-graph/README.md index b4b60e06b..5d515f32b 100644 --- a/recipes/ob-graph/README.md +++ b/recipes/ob-graph/README.md @@ -114,6 +114,7 @@ Done when: Your AI client can connect to the OB-Graph MCP server without authent Try these commands with your AI: **Build a small graph:** + ``` Create these graph nodes: - "Supabase" (type: tool) @@ -127,6 +128,7 @@ Then connect them: ``` **Query relationships:** + ``` What are all the neighbors of "Open Brain" in my graph? ``` From 2dca89069c45f59ee6a46b877e1a3b9f59270ae3 Mon Sep 17 00:00:00 2001 From: Alan Shurafa Date: Sat, 18 Apr 2026 20:17:04 -0400 Subject: [PATCH 043/125] [recipes] Fix markdownlint MD028: separate NOTE and WARNING blockquotes with HTML comment --- recipes/lint-sweep/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recipes/lint-sweep/README.md b/recipes/lint-sweep/README.md index 27e1dd014..e2de963de 100644 --- a/recipes/lint-sweep/README.md +++ b/recipes/lint-sweep/README.md @@ -70,6 +70,8 @@ OPTIONAL (TIER 3 ONLY) > [!NOTE] > This recipe now uses the standard `SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` variable names used by every other OB1 recipe, so a shared `.env.local` works across the whole repo. The legacy `OPEN_BRAIN_URL` / `OPEN_BRAIN_SERVICE_KEY` names are still accepted (with a deprecation warning) for backward compatibility. + + > [!WARNING] > The service role key bypasses Row Level Security. Keep `.env.local` out of version control and restrict its file permissions. From 686d2fa4c4420689a9c009460f04fc3f4da81ad3 Mon Sep 17 00:00:00 2001 From: spiritualsystems <145227240+spiritualsystems@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:44:51 -1000 Subject: [PATCH 044/125] [schemas] Fix invalid || concatenation in typed-reasoning-edges COMMENT PostgreSQL does not accept `||` string concatenation inside a `COMMENT ON ... IS ` expression; the RHS must be a single string literal. As written, `supabase db push` fails with SQLSTATE 42601 (syntax error at or near "||") and rolls back the whole migration. Flatten the three concatenated strings into one. Co-Authored-By: Claude Opus 4.7 (1M context) --- schemas/typed-reasoning-edges/schema.sql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/schemas/typed-reasoning-edges/schema.sql b/schemas/typed-reasoning-edges/schema.sql index b77abae3d..002a15c18 100644 --- a/schemas/typed-reasoning-edges/schema.sql +++ b/schemas/typed-reasoning-edges/schema.sql @@ -255,9 +255,7 @@ END; $$; COMMENT ON FUNCTION public.thought_edges_upsert IS - 'Insert or (on duplicate key) bump support_count + refresh temporal bounds. ' || - 'Call via POST /rpc/thought_edges_upsert. Use instead of a plain INSERT when ' || - 'you want repeated classifications of the same pair to accumulate evidence.'; + 'Insert or (on duplicate key) bump support_count + refresh temporal bounds. Call via POST /rpc/thought_edges_upsert. Use instead of a plain INSERT when you want repeated classifications of the same pair to accumulate evidence.'; REVOKE ALL ON FUNCTION public.thought_edges_upsert( UUID, UUID, TEXT, NUMERIC, INT, TEXT, TIMESTAMPTZ, TIMESTAMPTZ, JSONB From 49ebfbcae9ee6c235081271378aab7eb4797d146 Mon Sep 17 00:00:00 2001 From: Scott Hutchinson Date: Thu, 23 Apr 2026 10:18:47 -0500 Subject: [PATCH 045/125] [integrations] update_thought MCP with if_unchanged_since MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a standalone MCP Edge Function that exposes a single update_thought tool. Supports shallow metadata merges, content replacement with re-embedding, and optional if_unchanged_since optimistic concurrency for multi-writer setups. Deploys as its own Supabase Edge Function and registers as a separate custom connector — the core server/index.ts is untouched. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/update-thought-mcp/README.md | 126 ++++++++ integrations/update-thought-mcp/deno.json | 5 + integrations/update-thought-mcp/index.ts | 271 ++++++++++++++++++ integrations/update-thought-mcp/metadata.json | 20 ++ 4 files changed, 422 insertions(+) create mode 100644 integrations/update-thought-mcp/README.md create mode 100644 integrations/update-thought-mcp/deno.json create mode 100644 integrations/update-thought-mcp/index.ts create mode 100644 integrations/update-thought-mcp/metadata.json diff --git a/integrations/update-thought-mcp/README.md b/integrations/update-thought-mcp/README.md new file mode 100644 index 000000000..561ac2ed5 --- /dev/null +++ b/integrations/update-thought-mcp/README.md @@ -0,0 +1,126 @@ +# Update Thought MCP + +> Standalone MCP Edge Function that adds an `update_thought` tool with optional `if_unchanged_since` optimistic concurrency for multi-writer setups. + +## What It Does + +The core Open Brain MCP server captures, searches, lists, and summarises thoughts but does not expose an update path. This integration adds a single new tool, `update_thought`, deployable as a separate Supabase Edge Function and registered as its own custom connector alongside your main Open Brain connector. + +The tool supports three arguments: + +- `content` — when provided, overwrites the thought's text and regenerates its embedding via OpenRouter. +- `metadata_patch` — shallow-merged into the existing `metadata` JSONB. Keys not present in the patch are left alone. +- `if_unchanged_since` — optional ISO 8601 timestamp. When supplied, the update is rejected with `STALE_READ` if the stored `updated_at` has advanced past that reference. Omit for last-write-wins behaviour (backward compatible). + +Why it matters: once more than one agent writes to the same Open Brain (Claude Desktop, Codex, a background worker, etc.), last-write-wins silently drops concurrent edits. Optimistic concurrency is the cheapest fix — pass the `updated_at` you read, and the server rejects the write if something changed in between. + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) +- Supabase CLI installed (`npm i -g supabase` or your preferred method) +- [Deno](https://deno.land/) runtime available locally for type-checking (optional but recommended) +- OpenRouter API key (only required when your callers pass `content` — needed for re-embedding) + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +UPDATE THOUGHT MCP -- CREDENTIAL TRACKER +-------------------------------------- + +FROM YOUR OPEN BRAIN SETUP + Project URL: ____________ + Service role key: ____________ + OpenRouter API key: ____________ + MCP access key: ____________ + +GENERATED DURING SETUP + Update Thought URL: https://.supabase.co/functions/v1/update-thought-mcp + Custom connector name: Open Brain — Update + +-------------------------------------- +``` + +## Steps + +### 1. Create the Edge Function in your project + +From the root of your local Open Brain repo (the one you set up during getting-started): + +**1. Create the function folder:** + +```bash +supabase functions new update-thought-mcp +``` + +**2. Copy the integration code:** + +```bash +curl -o supabase/functions/update-thought-mcp/index.ts \ + https://raw.githubusercontent.com/NateBJones-Projects/OB1/main/integrations/update-thought-mcp/index.ts +curl -o supabase/functions/update-thought-mcp/deno.json \ + https://raw.githubusercontent.com/NateBJones-Projects/OB1/main/integrations/update-thought-mcp/deno.json +``` + +### 2. Set environment variables + +Reuse the same secrets as the core Open Brain server: + +```bash +supabase secrets set \ + OPENROUTER_API_KEY="your-openrouter-key" \ + MCP_ACCESS_KEY="your-mcp-access-key" +``` + +`SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are injected automatically by the platform. + +### 3. Deploy + +```bash +supabase functions deploy update-thought-mcp --no-verify-jwt +``` + +### 4. Register the connector in Claude Desktop + +Open **Settings → Connectors → Add custom connector** and paste: + +``` +https://.supabase.co/functions/v1/update-thought-mcp?key= +``` + +Name it something distinct from your main Open Brain connector (e.g. `Open Brain — Update`) so the tool shows up clearly in your tool list. + +### 5. Verify + +Ask Claude: `Call the update_thought tool with id = "" and metadata_patch = {"reviewed": true}.` You should see a success message and the thought's `updated_at` timestamp advance. + +To verify optimistic concurrency: + +1. Read a thought and note its `updated_at` (call it T0). +2. Call `update_thought` with `if_unchanged_since = T0` — it succeeds. `updated_at` is now T1. +3. Call `update_thought` again with `if_unchanged_since = T0` — it is rejected with `STALE_READ`. + +## Expected Outcome + +- A new Edge Function at `https://.supabase.co/functions/v1/update-thought-mcp`. +- A custom connector registered in your AI client that exposes exactly one tool, `update_thought`. +- Updating an existing thought replaces its content, re-embeds it, or merges a metadata patch. +- When `if_unchanged_since` is passed, the server rejects writes that would overwrite a concurrent change with a `STALE_READ` error, giving the caller a clear signal to re-fetch and retry. + +The [MCP Tool Audit & Optimization Guide](../../docs/05-tool-audit.md) covers how to manage your tool surface area once you add this (and any other) custom connector. + +## Troubleshooting + +**Issue: Tool call returns `401 Invalid or missing access key`** +Solution: Make sure the `?key=` parameter in your connector URL matches the `MCP_ACCESS_KEY` secret you set with `supabase secrets set`. If you rotate the key, re-deploy the function and update the connector URL. + +**Issue: `OPENROUTER_API_KEY is not set on this Edge Function; content updates cannot re-embed.`** +Solution: This appears only when a caller passes `content`. Set the secret (`supabase secrets set OPENROUTER_API_KEY=...`) and re-deploy. Updates that only pass `metadata_patch` work without an embedding provider. + +**Issue: Updates always succeed even though I expected `STALE_READ`** +Solution: `if_unchanged_since` is optional. Confirm you are actually passing it, and that the timestamp you read was the thought's `updated_at` (not `created_at`). The default `update_updated_at` trigger from the getting-started guide keeps `updated_at` current on every write. + +## Attribution + +Adapted from a multi-participant capture design used across live Claude / ChatGPT / Codex sessions. Released here as a standalone integration so any Open Brain user can opt in without touching the core server. diff --git a/integrations/update-thought-mcp/deno.json b/integrations/update-thought-mcp/deno.json new file mode 100644 index 000000000..5f87fd0cc --- /dev/null +++ b/integrations/update-thought-mcp/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "@supabase/supabase-js": "npm:@supabase/supabase-js@2.47.10" + } +} diff --git a/integrations/update-thought-mcp/index.ts b/integrations/update-thought-mcp/index.ts new file mode 100644 index 000000000..7c4853224 --- /dev/null +++ b/integrations/update-thought-mcp/index.ts @@ -0,0 +1,271 @@ +/** + * update-thought-mcp — Standalone MCP Edge Function that adds a single tool: + * update_thought(id, content?, metadata_patch?, if_unchanged_since?) + * + * Why a separate Edge Function? + * The core `open-brain` MCP server (server/index.ts) is curated and does not + * expose an update path. This integration adds one without modifying the + * core server. Deploy it alongside your main MCP connector and register it + * as a separate custom connector in Claude Desktop (or your client of + * choice). + * + * Behavior: + * - `content` — when provided, overwrites the thought text and regenerates + * the embedding. Omit to leave content unchanged. + * - `metadata_patch` — shallow-merged into the existing metadata JSONB. + * Keys not present in the patch are left alone. + * - `if_unchanged_since` — optional ISO 8601 timestamp (with offset). When + * provided, the update is rejected with a STALE_READ error if the stored + * `updated_at` has advanced past that reference. Omit for last-write-wins + * behavior (backward compatible). + * + * Auth: x-brain-key header OR ?key=... URL query parameter (same pattern as + * the core server — see server/index.ts). + * + * Env vars: + * SUPABASE_URL + * SUPABASE_SERVICE_ROLE_KEY + * OPENROUTER_API_KEY — only used when `content` is provided + * MCP_ACCESS_KEY + */ + +import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPTransport } from "@hono/mcp"; +import { Hono } from "hono"; +import { z } from "zod"; +import { createClient } from "@supabase/supabase-js"; + +const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; +const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; +const OPENROUTER_API_KEY = Deno.env.get("OPENROUTER_API_KEY") ?? ""; +const MCP_ACCESS_KEY = Deno.env.get("MCP_ACCESS_KEY")!; + +const OPENROUTER_BASE = "https://openrouter.ai/api/v1"; +const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY); + +async function getEmbedding(text: string): Promise { + const r = await fetch(`${OPENROUTER_BASE}/embeddings`, { + method: "POST", + headers: { + Authorization: `Bearer ${OPENROUTER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: "openai/text-embedding-3-small", + input: text, + }), + }); + if (!r.ok) { + const msg = await r.text().catch(() => ""); + throw new Error(`OpenRouter embeddings failed: ${r.status} ${msg}`); + } + const d = await r.json(); + return d.data[0].embedding; +} + +// --- MCP Server Setup --- + +const server = new McpServer({ + name: "open-brain-update-thought", + version: "1.0.0", +}); + +server.registerTool( + "update_thought", + { + title: "Update Thought", + description: + "Update an existing thought by ID. Provide `content` to overwrite the text and regenerate its embedding, `metadata_patch` to shallow-merge changes into the existing metadata, or both. Keys not mentioned in `metadata_patch` are left unchanged. Pass `if_unchanged_since` (ISO 8601 timestamp from your last read) for optimistic concurrency — the update is rejected with STALE_READ if another writer has touched the row since then.", + inputSchema: { + id: z.string().uuid().describe("UUID of the thought to update"), + content: z + .string() + .min(1) + .max(50_000) + .optional() + .describe("New text content — triggers re-embedding when provided"), + metadata_patch: z + .record(z.unknown()) + .optional() + .describe( + "Partial metadata to shallow-merge into the existing metadata JSONB. New keys are added; existing keys are overwritten; keys not mentioned are left alone.", + ), + if_unchanged_since: z + .string() + .datetime({ offset: true }) + .optional() + .describe( + "Optional ISO 8601 timestamp (with timezone). When provided, the update is rejected with STALE_READ if the stored updated_at has advanced past this reference. Pass the updated_at value from your most recent read to guard against lost-update conflicts. Omit to keep last-write-wins behavior.", + ), + }, + }, + async ({ id, content, metadata_patch, if_unchanged_since }) => { + try { + // Fetch existing row. We need updated_at for the concurrency check and + // metadata for the shallow-merge. + const { data: existing, error: fetchError } = await supabase + .from("thoughts") + .select("id, content, metadata, created_at, updated_at") + .eq("id", id) + .single(); + + if (fetchError || !existing) { + return { + content: [ + { + type: "text" as const, + text: `Thought not found: ${id}`, + }, + ], + isError: true, + }; + } + + // Optimistic concurrency check. Reject if stored updated_at is strictly + // newer than the caller's reference timestamp. + if (if_unchanged_since) { + const storedMs = new Date( + (existing.updated_at as string) ?? (existing.created_at as string), + ).getTime(); + const clientMs = new Date(if_unchanged_since).getTime(); + if ( + Number.isFinite(storedMs) && + Number.isFinite(clientMs) && + storedMs > clientMs + ) { + return { + content: [ + { + type: "text" as const, + text: + `STALE_READ: thought has been modified since ${if_unchanged_since}. ` + + `Current updated_at: ${existing.updated_at}. Re-fetch and retry.`, + }, + ], + isError: true, + }; + } + } + + const updates: Record = {}; + + if (content !== undefined) { + if (!OPENROUTER_API_KEY) { + return { + content: [ + { + type: "text" as const, + text: + "OPENROUTER_API_KEY is not set on this Edge Function; content updates cannot re-embed.", + }, + ], + isError: true, + }; + } + const embedding = await getEmbedding(content); + updates.content = content; + updates.embedding = `[${embedding.join(",")}]`; + } + + if (metadata_patch !== undefined) { + const merged = { + ...((existing.metadata as Record) || {}), + ...metadata_patch, + }; + updates.metadata = merged; + } + + if (Object.keys(updates).length === 0) { + return { + content: [ + { + type: "text" as const, + text: `No changes supplied; thought ${id} unchanged.`, + }, + ], + }; + } + + const { data, error } = await supabase + .from("thoughts") + .update(updates) + .eq("id", id) + .select("id, content, metadata, created_at, updated_at") + .single(); + + if (error) { + return { + content: [ + { + type: "text" as const, + text: `update_thought error: ${error.message}`, + }, + ], + isError: true, + }; + } + + const parts = [ + `Updated thought ${data.id}`, + content !== undefined ? " · content replaced and re-embedded" : null, + metadata_patch !== undefined ? " · metadata merged" : null, + ` · updated_at: ${data.updated_at}`, + ].filter(Boolean); + + return { + content: [{ type: "text" as const, text: parts.join("\n") }], + }; + } catch (err: unknown) { + return { + content: [ + { type: "text" as const, text: `Error: ${(err as Error).message}` }, + ], + isError: true, + }; + } + }, +); + +// --- Hono app with auth + CORS --- + +const corsHeaders = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": + "authorization, x-client-info, apikey, content-type, x-brain-key, accept, mcp-session-id", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS, DELETE", +}; + +const app = new Hono(); + +app.options("*", (c) => c.text("ok", 200, corsHeaders)); + +app.all("*", async (c) => { + const provided = + c.req.header("x-brain-key") || new URL(c.req.url).searchParams.get("key"); + if (!provided || provided !== MCP_ACCESS_KEY) { + return c.json({ error: "Invalid or missing access key" }, 401, corsHeaders); + } + + // Same Accept-header workaround as the core server — Claude Desktop's custom + // connectors do not send `text/event-stream` by default. + if (!c.req.header("accept")?.includes("text/event-stream")) { + const headers = new Headers(c.req.raw.headers); + headers.set("Accept", "application/json, text/event-stream"); + const patched = new Request(c.req.raw.url, { + method: c.req.raw.method, + headers, + body: c.req.raw.body, + // @ts-ignore -- duplex required for streaming body in Deno + duplex: "half", + }); + Object.defineProperty(c.req, "raw", { value: patched, writable: true }); + } + + const transport = new StreamableHTTPTransport(); + await server.connect(transport); + return transport.handleRequest(c); +}); + +Deno.serve(app.fetch); diff --git a/integrations/update-thought-mcp/metadata.json b/integrations/update-thought-mcp/metadata.json new file mode 100644 index 000000000..2590cec77 --- /dev/null +++ b/integrations/update-thought-mcp/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Update Thought MCP", + "description": "Standalone MCP Edge Function that adds an update_thought tool with optional if_unchanged_since optimistic concurrency for multi-writer setups.", + "category": "integrations", + "author": { + "name": "Scott Hutchinson", + "github": "txcfi-scott" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Supabase", "OpenRouter"], + "tools": ["Supabase CLI", "Deno"] + }, + "tags": ["mcp", "update", "concurrency", "multi-writer", "edge-function"], + "difficulty": "intermediate", + "estimated_time": "15 minutes", + "created": "2026-04-23", + "updated": "2026-04-23" +} From 13629bdd540a9b3307a5687151a2a76d33202297 Mon Sep 17 00:00:00 2001 From: Scott Hutchinson Date: Thu, 23 Apr 2026 10:23:00 -0500 Subject: [PATCH 046/125] [schemas] thought_audit table for multi-participant attribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds thought_audit: an append-only log of every capture/update/delete on the thoughts table, with source + author_session_id for attribution across Claude Desktop / Codex / ChatGPT / importer writes. Strictly additive — no existing thoughts columns altered, no existing functions replaced. Grants are INSERT-only, so nothing downstream can rewrite history without an explicit migration. An optional second SQL file adds a thought_provenance view and a thoughts_by_session() RPC for querying by session id. Co-Authored-By: Claude Opus 4.7 (1M context) --- schemas/thought-audit/README.md | 155 ++++++++++++++++++++ schemas/thought-audit/author-session-id.sql | 61 ++++++++ schemas/thought-audit/metadata.json | 20 +++ schemas/thought-audit/schema.sql | 114 ++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 schemas/thought-audit/README.md create mode 100644 schemas/thought-audit/author-session-id.sql create mode 100644 schemas/thought-audit/metadata.json create mode 100644 schemas/thought-audit/schema.sql diff --git a/schemas/thought-audit/README.md b/schemas/thought-audit/README.md new file mode 100644 index 000000000..40d224c48 --- /dev/null +++ b/schemas/thought-audit/README.md @@ -0,0 +1,155 @@ +# Thought Audit + +> Append-only audit table capturing every `capture` / `update` / `delete` on the thoughts table, plus an `author_session_id` convention for multi-participant attribution. + +## What It Does + +Adds one new table (`thought_audit`) and a couple of optional query helpers. The goal is to make "who changed what, when" answerable once more than one agent is writing to your Open Brain — Claude Desktop, Codex, ChatGPT via the API, a background importer, or any combination of the above. + +The schema is strictly **additive**. No existing columns on the `thoughts` table are altered or dropped. Existing functions (`match_thoughts`, `upsert_thought`) are not replaced. Every statement is idempotent and safe to re-run. + +**Key pieces:** + +1. **`thought_audit` table.** Append-only. Records an action (`capture` / `update` / `delete`), a source tag, an optional session id, and a compact JSONB diff — for deletes, the prior content is preserved so the event is recoverable from the audit log alone. +2. **Deliberate non-FK.** `thought_audit.thought_id` has no foreign key to `thoughts(id)`. Audit rows must outlive the thoughts they describe. +3. **Grants are INSERT-only.** The audit table is append-only by design. `service_role` gets `SELECT, INSERT` — never `UPDATE` or `DELETE`. Nothing downstream can rewrite history without an explicit migration. +4. **`author_session_id` convention.** A short opaque string your capture tools tuck into `thoughts.metadata.author_session_id`. No schema change needed — it is just a convention on the existing JSONB column. The optional second SQL file adds a helper view and RPC for querying by session. + +**Example audit timeline:** + +``` +t=10:00 capture_thought source=claude-desktop session=sess-AAA id=thought-123 +t=10:05 update_thought source=claude-desktop session=sess-AAA id=thought-123 (metadata patch) +t=11:30 update_thought source=chatgpt-api session=sess-BBB id=thought-123 (content rewrite) +t=15:00 delete_thought source=codex-cli session=sess-CCC id=thought-123 (previous_content preserved) +``` + +A single query against `thought_audit` where `thought_id = 'thought-123'` returns the full provenance. + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +THOUGHT AUDIT -- CREDENTIAL TRACKER +-------------------------------------- + +SUPABASE (from your Open Brain setup) + Project URL: ____________ + Service role key: ____________ + +-------------------------------------- +``` + +## Steps + +1. Open your Supabase dashboard → **SQL Editor → New query**. +2. Paste the full contents of `schema.sql` and click **Run**. This creates the `thought_audit` table, its indexes, RLS, and the INSERT-only grant for `service_role`. +3. *(Optional)* Open a new query, paste the full contents of `author-session-id.sql`, and click **Run**. This creates the `thought_provenance` view and `thoughts_by_session()` RPC used to query by session id. +4. **Wire audit writes into your mutation tools.** This schema is storage only — no trigger, no hidden magic. You (or a mutation integration like `integrations/update-thought-mcp` and `integrations/delete-thought-mcp`) are responsible for inserting a row after each capture / update / delete. See the "How to write audit rows" section below for copy-paste examples. +5. Navigate to **Table Editor** → confirm `thought_audit` appears with columns `id, thought_id, action, source, author_session_id, diff, actor_context, created_at`. +6. Run `insert into thought_audit (thought_id, action, source) values (gen_random_uuid(), 'capture', 'manual-test');` then `select * from thought_audit order by created_at desc limit 1;` to confirm writes land. + +## Expected Outcome + +- A new `thought_audit` table with three indexes (`thought_id`, `author_session_id`, `created_at desc`). +- `service_role` holds `SELECT, INSERT` on the table — **not** `UPDATE` or `DELETE`. This is deliberate: the audit log is append-only. +- RLS is enabled (service_role bypasses RLS by default, matching the existing project convention). +- *(If you ran `author-session-id.sql`)* A read-only `thought_provenance` view and a `thoughts_by_session(p_session_id text)` RPC. + +## How To Write Audit Rows + +The audit table is purely storage. Your capture / update / delete tools insert rows. Here are the three patterns — keep them fire-and-forget so a failure in the audit insert never blocks the main operation. + +**On capture:** + +```ts +await supabase.from("thought_audit").insert({ + thought_id: newThoughtId, + action: "capture", + source: input.source ?? "claude-desktop", + author_session_id: input.author_session_id ?? null, + diff: { content_length: content.length, type: extractedType }, + actor_context: { origin: "mcp:capture_thought" }, +}); +``` + +**On update (see `integrations/update-thought-mcp`):** + +```ts +await supabase.from("thought_audit").insert({ + thought_id: id, + action: "update", + source: afterMetadata.source ?? beforeMetadata.source ?? null, + author_session_id: afterMetadata.author_session_id ?? beforeMetadata.author_session_id ?? null, + diff: { + content: contentChanged ? { before_length: before.length, after_length: after.length } : null, + metadata: metadataDiff(beforeMetadata, afterMetadata), + }, + actor_context: { origin: "mcp:update_thought" }, +}); +``` + +**On delete (see `integrations/delete-thought-mcp`):** + +```ts +await supabase.from("thought_audit").insert({ + thought_id: id, + action: "delete", + source: priorMetadata.source ?? null, + author_session_id: priorMetadata.author_session_id ?? null, + diff: { + previous_content: priorContent, + previous_metadata: priorMetadata, + }, + actor_context: { origin: "mcp:delete_thought" }, +}); +``` + +All three are **best-effort**. Wrap them in `try { ... } catch { console.warn(...) }` so an audit write failure never surfaces to the caller — the original operation already succeeded by the time you reach the audit insert. + +## The `author_session_id` Convention + +This is just a string you tuck into `thoughts.metadata.author_session_id` at capture time. Nothing enforces it — if you do not write it, queries that filter on it simply return nothing for that thought. + +**Suggested generation pattern:** + +```ts +// At session start (agent boot, REPL load, chat thread open): +const author_session_id = `sess-${crypto.randomUUID().slice(0, 8)}`; +``` + +**Suggested source tags** (adopted by the reference integrations): + +| Tag | Meaning | +| --- | ------- | +| `claude-desktop` | Claude Desktop chat, via custom connectors | +| `claude-code-live` | Claude Code CLI, human-in-the-loop | +| `chatgpt-live` | ChatGPT UI, user-driven captures | +| `chatgpt-api` | Automation using the OpenAI API | +| `codex-cli` | Codex CLI sessions | +| `ingest-worker` | Background ingest jobs (email, docs, imports) | + +The source column is an open `text` — feel free to add your own. Keep them short and hyphen-separated so queries stay readable. + +## Dependencies and Companions + +- `integrations/update-thought-mcp` — adds an `update_thought` MCP tool. Its README documents how to extend it to write audit rows. +- `integrations/delete-thought-mcp` — adds a `delete_thought` MCP tool. Its README documents how to extend it to write audit rows with preserved prior content. + +Both are standalone — installing this schema without those integrations is perfectly valid (you can write audit rows from your own capture / mutation code instead). + +## Troubleshooting + +**Issue: `permission denied for table thought_audit` when inserting from an Edge Function** +Solution: Re-run the `GRANT SELECT, INSERT ON TABLE public.thought_audit TO service_role;` statement from `schema.sql`. Supabase does not grant CRUD on new tables to `service_role` by default. + +**Issue: Trying to `DELETE FROM thought_audit` fails** +Solution: This is intentional. `service_role` is granted only `SELECT, INSERT`. If you need to prune old rows, write a deliberate migration that temporarily grants `DELETE`, removes the rows with a precise `WHERE`, and revokes again. That way pruning is a reviewed operation, not an accident. + +**Issue: `author_session_id` is always NULL in the audit rows** +Solution: The audit table stores whatever the caller inserts. If your capture tools do not set `metadata.author_session_id` (or do not pass it to the audit insert), the column stays NULL. That is fine — legacy traffic is allowed to predate the convention. Start tagging new writes and those sessions will become queryable immediately. diff --git a/schemas/thought-audit/author-session-id.sql b/schemas/thought-audit/author-session-id.sql new file mode 100644 index 000000000..c47fd1ebe --- /dev/null +++ b/schemas/thought-audit/author-session-id.sql @@ -0,0 +1,61 @@ +-- ============================================================ +-- author_session_id — multi-participant attribution helper +-- +-- This is a CONVENTION, not a schema change. It lives entirely in +-- the existing thoughts.metadata JSONB column. This file is two +-- small helper views/functions for querying by session, plus +-- documentation via COMMENT ON. +-- +-- Convention: +-- thoughts.metadata.author_session_id — short opaque string +-- generated at session start by the capturing agent. Groups +-- all writes from the same live session (Claude Desktop chat, +-- a Codex run, a ChatGPT conversation, an import job). +-- +-- thoughts.metadata.source — short hyphenated tag identifying +-- the agent or integration (e.g. "claude-code-live", +-- "chatgpt-api", "codex-cli"). +-- +-- Both fields are optional. Nothing breaks if they are missing — +-- this is purely additive. +-- ============================================================ + +-- Helper view: flatten author_session_id / source out of metadata +-- for the common "what did this session do" query. Safe to create +-- more than once. +CREATE OR REPLACE VIEW thought_provenance AS +SELECT + t.id, + t.created_at, + t.updated_at, + (t.metadata ->> 'source') AS source, + (t.metadata ->> 'author_session_id') AS author_session_id, + t.content +FROM public.thoughts t; + +COMMENT ON VIEW thought_provenance IS + 'Convenience projection that surfaces metadata.source and metadata.author_session_id as top-level columns. Read-only; no effect on the underlying thoughts table.'; + +-- Helper RPC: list all thoughts in a given session, newest first. +CREATE OR REPLACE FUNCTION thoughts_by_session(p_session_id TEXT) +RETURNS TABLE ( + id UUID, + created_at TIMESTAMPTZ, + source TEXT, + content TEXT +) +LANGUAGE sql +STABLE +AS $$ + SELECT + t.id, + t.created_at, + (t.metadata ->> 'source') AS source, + t.content + FROM public.thoughts t + WHERE (t.metadata ->> 'author_session_id') = p_session_id + ORDER BY t.created_at DESC; +$$; + +COMMENT ON FUNCTION thoughts_by_session(TEXT) IS + 'Return all thoughts tagged with metadata.author_session_id = p_session_id, newest first. Used to reconstruct the writes from a single agent session.'; diff --git a/schemas/thought-audit/metadata.json b/schemas/thought-audit/metadata.json new file mode 100644 index 000000000..b4fbe101b --- /dev/null +++ b/schemas/thought-audit/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Thought Audit", + "description": "Append-only audit table capturing every capture / update / delete on the thoughts table, plus an author_session_id metadata convention for multi-participant attribution.", + "category": "schemas", + "author": { + "name": "Scott Hutchinson", + "github": "txcfi-scott" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Supabase"], + "tools": [] + }, + "tags": ["audit", "attribution", "multi-writer", "provenance", "schema"], + "difficulty": "intermediate", + "estimated_time": "15 minutes", + "created": "2026-04-23", + "updated": "2026-04-23" +} diff --git a/schemas/thought-audit/schema.sql b/schemas/thought-audit/schema.sql new file mode 100644 index 000000000..7a3e8cc4e --- /dev/null +++ b/schemas/thought-audit/schema.sql @@ -0,0 +1,114 @@ +-- ============================================================ +-- Thought Audit — append-only log of every capture / update / delete +-- +-- Rationale: once more than one participant writes to the same Open +-- Brain (Claude Desktop, Codex, ChatGPT via API, background workers), +-- "who changed what, and when" becomes an operational question. +-- This schema is the answer — an append-only table plus a metadata +-- convention (author_session_id) that ties related writes together. +-- +-- All changes are additive. No existing thoughts columns are altered +-- or dropped. No existing functions are replaced. +-- +-- Deliberate NON foreign key: thought_audit.thought_id has no FK +-- reference to thoughts(id). Audit rows MUST survive deletion of the +-- thought they describe — the delete itself is the most important +-- audit event to preserve. +-- +-- Safe to run more than once (fully idempotent). +-- ============================================================ + +CREATE TABLE IF NOT EXISTS thought_audit ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + -- The affected thought's UUID. NOT a foreign key — audit rows must + -- outlive the thoughts they describe (most importantly for delete + -- events). + thought_id UUID NOT NULL, + + -- Operation type. Constrained to the three mutation verbs. + action TEXT NOT NULL + CHECK (action IN ('capture', 'update', 'delete')), + + -- Source tag from metadata.source (or the request body) at the time + -- of the mutation. Duplicated here so "what did claude-code-live + -- change this week" queries work without joining on the (possibly + -- deleted) thoughts row. + -- + -- Source is an open string, not an enum — the convention used by the + -- reference integrations is a short dotted tag. Examples in use: + -- claude-desktop + -- claude-code-live + -- chatgpt-live + -- chatgpt-api + -- codex-cli + -- ingest-worker + -- Feel free to add your own. Keep them hyphen-separated lowercase. + source TEXT, + + -- Opaque participant session id. Enables "cluster all writes from + -- this agent session" queries. NULL when the caller did not supply + -- one (expected for legacy traffic and for tools that do not yet + -- use the multi-participant convention). + author_session_id TEXT, + + -- For `update`: a compact before/after diff of the content and + -- metadata keys that actually changed. + -- For `delete`: the full prior content preserved under + -- diff.previous_content and prior metadata under + -- diff.previous_metadata, so the row is recoverable from the + -- audit trail alone. + -- For `capture`: typically empty or a small metadata summary. + diff JSONB, + + -- Free-form contextual info (tool name, MCP client hint, extra + -- tags). Optional — analytical use only. + actor_context JSONB, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- Comments for dbdocs / Supabase UI +COMMENT ON TABLE thought_audit IS + 'Append-only audit log of every capture/update/delete on the thoughts table. thought_id is deliberately NOT an FK to thoughts(id) so audit rows survive deletion of their subject. Writes are fire-and-forget from the MCP server or ingest worker — failures here never block the main operation.'; + +COMMENT ON COLUMN thought_audit.thought_id IS + 'UUID of the affected thought. NOT a foreign key — audit rows outlive the thoughts they describe.'; + +COMMENT ON COLUMN thought_audit.action IS + 'Mutation verb: capture | update | delete.'; + +COMMENT ON COLUMN thought_audit.source IS + 'Source tag at time of mutation (e.g. claude-desktop, chatgpt-api, codex-cli). Duplicated from metadata.source so audit queries do not need to join thoughts.'; + +COMMENT ON COLUMN thought_audit.author_session_id IS + 'Opaque participant session identifier at time of mutation. NULL for legacy traffic that predates the multi-participant convention.'; + +COMMENT ON COLUMN thought_audit.diff IS + 'For update: before/after of changed fields. For delete: previous_content and previous_metadata preserved for recovery. For capture: post-insert metadata summary.'; + +COMMENT ON COLUMN thought_audit.actor_context IS + 'Optional free-form analytical metadata (tool name, MCP client hint, etc.).'; + +-- Indexes: the three most common audit access patterns. +CREATE INDEX IF NOT EXISTS thought_audit_thought_id_idx + ON thought_audit (thought_id); + +CREATE INDEX IF NOT EXISTS thought_audit_session_id_idx + ON thought_audit (author_session_id) + WHERE author_session_id IS NOT NULL; + +CREATE INDEX IF NOT EXISTS thought_audit_created_at_idx + ON thought_audit (created_at DESC); + +-- Match the project convention — service role bypasses RLS automatically. +ALTER TABLE thought_audit ENABLE ROW LEVEL SECURITY; + +-- Supabase no longer auto-grants CRUD on new tables to service_role. +-- Grant explicitly so the MCP server can write audit rows. +GRANT SELECT, INSERT ON TABLE public.thought_audit TO service_role; + +-- The audit table is append-only by intent. We do NOT grant UPDATE or +-- DELETE, so nothing — not even a buggy Edge Function — can rewrite +-- history. If you need to prune old rows, do it explicitly from a +-- migration after a conscious decision. From 3ae4cdfeba281c1835966751e343487da8759cff Mon Sep 17 00:00:00 2001 From: Scott Hutchinson Date: Thu, 23 Apr 2026 11:07:57 -0500 Subject: [PATCH 047/125] [schemas] add recency-boosted match_thoughts RPC variant Installs match_thoughts_recency alongside the core match_thoughts RPC. Blends cosine similarity with an exponential recency decay: recency_factor = exp(-age_days / half_life_days) final_score = similarity * (1 - recency_weight) + recency_factor * recency_weight Defaults (recency_weight = 0) reproduce the behavior of match_thoughts exactly, so installation is side-effect-free for existing callers. Threshold is applied on raw cosine similarity before the blend. --- .../recency-boosted-match-thoughts/README.md | 116 ++++++++++++++++++ .../metadata.json | 20 +++ .../recency-boosted-match-thoughts/schema.sql | 111 +++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 schemas/recency-boosted-match-thoughts/README.md create mode 100644 schemas/recency-boosted-match-thoughts/metadata.json create mode 100644 schemas/recency-boosted-match-thoughts/schema.sql diff --git a/schemas/recency-boosted-match-thoughts/README.md b/schemas/recency-boosted-match-thoughts/README.md new file mode 100644 index 000000000..01e420883 --- /dev/null +++ b/schemas/recency-boosted-match-thoughts/README.md @@ -0,0 +1,116 @@ +# Recency-Boosted match_thoughts RPC + +> Adds `match_thoughts_recency` — a variant of the core `match_thoughts` RPC that blends cosine similarity with an exponential recency decay, without replacing the original function. + +## What It Does + +Installs a new function, `match_thoughts_recency`, alongside the existing `match_thoughts` from the getting-started guide. It accepts the same arguments plus two new optional parameters, `recency_weight` and `half_life_days`, and returns the same columns. Ranking is computed as a blend of cosine similarity and an exponentially-decaying recency factor: + +``` +recency_factor = exp(-age_days / half_life_days) +final_score = similarity * (1 - recency_weight) + + recency_factor * recency_weight +``` + +With `recency_weight = 0` (the default), the function is identical in behavior to `match_thoughts`. The threshold is applied to the **raw** cosine similarity before the blend, so a high recency weight cannot surface completely irrelevant recent thoughts. + +## Why It Matters + +Pure cosine similarity returns ancient thoughts ranked high whenever they happen to be vector-nearest. For a personal knowledge base that is fine — old evergreen notes should surface. But for an active daily-context or task-tracking brain, a gentle recency preference produces visibly better results. This RPC makes that preference opt-in and tunable per query rather than baked into the schema. + +## Prerequisites + +- Working Open Brain setup (see [`docs/01-getting-started.md`](../../docs/01-getting-started.md)) +- The core `match_thoughts` function and `thoughts` table already installed + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +RECENCY-BOOSTED match_thoughts -- CREDENTIAL TRACKER +-------------------------------------- + +SUPABASE (from your Open Brain setup) + Project URL: ____________ + Secret key: ____________ + +-------------------------------------- +``` + +## Steps + +1. Open your Supabase dashboard and navigate to the **SQL Editor** +2. Create a new query and paste the full contents of `schema.sql` +3. Click **Run** to execute the migration +4. Navigate to **Database → Functions** and confirm `match_thoughts_recency` now appears alongside `match_thoughts` + +## Expected Outcome + +After running the migration: + +- A new function, `match_thoughts_recency`, is callable from the Supabase client or REST API. +- The existing `match_thoughts` function is untouched — any client code that calls it continues to behave exactly as before. +- Calling `match_thoughts_recency` with default arguments (`recency_weight = 0`) returns the same ranking as `match_thoughts`. + +## Example Usage + +### Pure similarity (identical to `match_thoughts`) + +```sql +select * +from match_thoughts_recency( + query_embedding := '[...]'::vector(1536), + match_threshold := 0.7, + match_count := 10 +); +``` + +### Gentle recency nudge + +```sql +select * +from match_thoughts_recency( + query_embedding := '[...]'::vector(1536), + match_threshold := 0.7, + match_count := 10, + recency_weight := 0.2, -- 20% weight on recency + half_life_days := 90.0 -- thoughts 90 days old count half as "recent" +); +``` + +### Strong recency preference (e.g. "what did I capture this week about X?") + +```sql +select * +from match_thoughts_recency( + query_embedding := '[...]'::vector(1536), + match_threshold := 0.6, + match_count := 10, + recency_weight := 0.7, + half_life_days := 14.0 +); +``` + +### Tuning notes + +- `recency_weight = 0.0` → pure similarity (default, backward-compatible). +- `recency_weight = 0.2` → gentle nudge toward recent thoughts. +- `recency_weight = 0.5` → even blend. +- `recency_weight = 1.0` → pure recency ranking (similarity still gates via threshold). +- Values outside `[0, 1]` are clamped. +- `half_life_days` must be positive; non-positive values fall back to 90. + +## Troubleshooting + +**Issue: "function match_thoughts_recency does not exist"** +Solution: Confirm `schema.sql` ran without errors in the SQL editor. The function installs into the `public` schema; check **Database → Functions** and filter by schema. + +**Issue: results look identical to `match_thoughts`** +Solution: You probably left `recency_weight` at its default (`0.0`). That is by design — pass a non-zero value (try `0.2`) to see the recency blend take effect. + +**Issue: very recent but irrelevant thoughts are appearing at the top** +Solution: Either raise `match_threshold` (the raw cosine floor) or lower `recency_weight`. The threshold gates on raw similarity before the blend, so a tighter threshold keeps noise out regardless of how strongly you weight recency. + +**Issue: I want this to replace `match_thoughts` entirely** +Solution: This schema is deliberately additive. If you want recency-boosted ranking as the default for every caller, change your client code to call `match_thoughts_recency` (same return columns) instead of editing the core function — that keeps this contribution safe to install on any Open Brain and easy to roll back. diff --git a/schemas/recency-boosted-match-thoughts/metadata.json b/schemas/recency-boosted-match-thoughts/metadata.json new file mode 100644 index 000000000..7472c7fcd --- /dev/null +++ b/schemas/recency-boosted-match-thoughts/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Recency-Boosted match_thoughts", + "description": "Adds match_thoughts_recency — a variant of the core match_thoughts RPC that blends cosine similarity with an exponential recency decay. Defaults are backward-compatible with pure similarity.", + "category": "schemas", + "author": { + "name": "Scott Hutchinson", + "github": "txcfi-scott" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Supabase"], + "tools": [] + }, + "tags": ["search", "recency", "ranking", "pgvector", "rpc"], + "difficulty": "intermediate", + "estimated_time": "10 minutes", + "created": "2026-04-23", + "updated": "2026-04-23" +} diff --git a/schemas/recency-boosted-match-thoughts/schema.sql b/schemas/recency-boosted-match-thoughts/schema.sql new file mode 100644 index 000000000..103243e48 --- /dev/null +++ b/schemas/recency-boosted-match-thoughts/schema.sql @@ -0,0 +1,111 @@ +-- ============================================================ +-- match_thoughts_recency — recency-boosted variant of match_thoughts +-- +-- Problem: +-- The core match_thoughts RPC (from the getting-started guide) +-- ranks purely by cosine similarity. For a growing Open Brain, +-- this means very old thoughts that happen to be vector-nearest +-- outrank newer, arguably more relevant thoughts of similar +-- quality. For an evergreen personal memory that can stay as-is, +-- but for an active task-tracking or daily-context brain, a +-- gentle recency preference produces visibly better results. +-- +-- Solution: +-- A separate RPC, match_thoughts_recency, that returns the same +-- columns as match_thoughts but blends similarity with an +-- exponential recency decay. The ORIGINAL match_thoughts is +-- NOT replaced — callers opt into the new variant by name. +-- +-- Formula: +-- recency_factor = exp(-age_days / half_life_days) +-- final_score = similarity * (1 - recency_weight) +-- + recency_factor * recency_weight +-- +-- Defaults are deliberately backward-compatible: +-- recency_weight = 0 → final_score = similarity (identical ranking) +-- half_life_days = 90 → ignored while recency_weight = 0 +-- +-- Set recency_weight to e.g. 0.2 for a gentle nudge toward recent +-- thoughts, 0.5 for an even blend, 1.0 for pure recency ranking. +-- +-- This file only adds a new function. No existing columns or +-- functions are altered. Safe to run more than once. +-- ============================================================ + +SET search_path TO public, extensions; + +CREATE OR REPLACE FUNCTION match_thoughts_recency( + query_embedding vector(1536), + match_threshold float DEFAULT 0.7, + match_count int DEFAULT 10, + filter jsonb DEFAULT '{}'::jsonb, + recency_weight float DEFAULT 0.0, -- 0 = disabled (same as match_thoughts) + half_life_days float DEFAULT 90.0 -- only consulted when recency_weight > 0 +) +RETURNS TABLE ( + id uuid, + content text, + metadata jsonb, + similarity float, + created_at timestamptz +) +LANGUAGE plpgsql +STABLE +PARALLEL SAFE +AS $$ +BEGIN + -- Clamp inputs to sensible ranges. The CREATE OR REPLACE will not + -- recurse, but an over-eager caller passing recency_weight = 5.0 + -- should not blow up the ranking — cap it at 1.0. + IF recency_weight < 0.0 THEN recency_weight := 0.0; END IF; + IF recency_weight > 1.0 THEN recency_weight := 1.0; END IF; + IF half_life_days <= 0.0 THEN half_life_days := 90.0; END IF; + + RETURN QUERY + SELECT + t.id, + t.content, + t.metadata, + -- Blended score: similarity * (1 - w) + recency_factor * w + -- recency_factor = exp(-age_days / half_life_days) + -- age_days comes from extract(epoch) converted to days. + ( + (1 - (t.embedding <=> query_embedding)) * (1.0 - recency_weight) + + + exp( + -GREATEST( + extract(epoch FROM (now() - t.created_at)) / 86400.0, + 0.0 + ) / half_life_days + ) * recency_weight + )::float AS similarity, + t.created_at + FROM public.thoughts t + WHERE + -- Threshold gates on the RAW cosine similarity so its semantics + -- stay consistent with the core match_thoughts RPC. Without this, + -- a high recency weight would let completely irrelevant recent + -- thoughts surface. + (1 - (t.embedding <=> query_embedding)) >= match_threshold + -- Metadata containment filter — empty '{}' matches everything. + AND (filter = '{}'::jsonb OR t.metadata @> filter) + ORDER BY + -- Order by the same blended score returned in the similarity + -- column, so the client gets results ranked exactly as the + -- similarity value implies. + ( + (1 - (t.embedding <=> query_embedding)) * (1.0 - recency_weight) + + + exp( + -GREATEST( + extract(epoch FROM (now() - t.created_at)) / 86400.0, + 0.0 + ) / half_life_days + ) * recency_weight + ) DESC + LIMIT match_count; +END; +$$; + +COMMENT ON FUNCTION match_thoughts_recency(vector(1536), float, int, jsonb, float, float) IS + 'Recency-boosted nearest-neighbor search. Blended score = similarity * (1 - recency_weight) + exp(-age_days/half_life_days) * recency_weight. recency_weight defaults to 0 (pure similarity, identical to match_thoughts). half_life_days defaults to 90. Threshold is applied on raw cosine similarity before the blend. Returns the same columns as match_thoughts.'; From 8a9e70cf454e7cb8a755ae505cf6c29e00065a20 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 07:40:40 +1000 Subject: [PATCH 048/125] [schemas] Add readwise-books cache table and helper RPCs Adds a side-table of Readwise book-level metadata keyed by user_book_id, so highlights stored in thoughts can reference a book without denormalising title/author into every highlight row. Includes two RPCs: - get_book_highlights: returns a book's highlights in in-source location order so a reader can review them the way they were encountered. - increment_book_highlight_count: keeps num_highlights and last_highlight_at fresh without a COUNT over thoughts. Required by the forthcoming readwise-capture integration and readwise-import recipe. Co-Authored-By: Claude Opus 4.7 (1M context) --- schemas/readwise-books/README.md | 67 +++++++++++++++++ schemas/readwise-books/metadata.json | 20 +++++ schemas/readwise-books/schema.sql | 107 +++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 schemas/readwise-books/README.md create mode 100644 schemas/readwise-books/metadata.json create mode 100644 schemas/readwise-books/schema.sql diff --git a/schemas/readwise-books/README.md b/schemas/readwise-books/README.md new file mode 100644 index 000000000..24d390766 --- /dev/null +++ b/schemas/readwise-books/README.md @@ -0,0 +1,67 @@ +# Readwise Books Cache + +> Side-table cache of Readwise book-level metadata so highlights stored in `thoughts` can reference a `book_id` without denormalising title and author into every highlight row. + +## What It Does + +This schema extension creates a single new table, `readwise_books`, keyed by Readwise's `user_book_id`, plus two RPC functions: + +- **`get_book_highlights(p_book_id, p_limit)`** — Returns all highlights for a book from the `thoughts` table, ordered by in-source location so you can re-read them the way you originally encountered them. +- **`increment_book_highlight_count(p_book_id, p_highlighted_at)`** — Bumps `num_highlights` and refreshes `last_highlight_at` on the book row. Called by the readwise-capture Edge Function on each new highlight insert to avoid running `COUNT(*)` against the `thoughts` table. + +The table stays small (one row per book, typically a few hundred rows per user), so it's primarily a convenience cache for cover images, category, and source attribution that would otherwise need to be fetched from the Readwise API on every UI render. + +This schema is required by: + +- [integrations/readwise-capture](../../integrations/readwise-capture/) — webhook receiver for live highlight capture +- [recipes/readwise-import](../../recipes/readwise-import/) — one-shot backfill of your Readwise history + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) +- Supabase project with the `thoughts` table already created (from the core setup) +- Recommended: [enhanced-thoughts](../enhanced-thoughts/) schema applied first — `get_book_highlights` filters on `source_type = 'readwise'`, which is a top-level column added by that extension. Without it, the RPC will not return rows. + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +READWISE BOOKS -- CREDENTIAL TRACKER +-------------------------------------- + +SUPABASE (from your Open Brain setup) + Project URL: ____________ + Secret key: ____________ + +-------------------------------------- +``` + +## Steps + +1. Open your Supabase dashboard and navigate to the **SQL Editor** +2. Create a new query and paste the full contents of `schema.sql` +3. Click **Run** to execute the migration +4. Open **Table Editor** and confirm the `readwise_books` table appears with the expected columns +5. Navigate to **Database > Functions** and verify two new functions exist: `get_book_highlights`, `increment_book_highlight_count` + +## Expected Outcome + +After running the migration: + +- A new `readwise_books` table with `book_id` as the primary key and indexes on `title`, `author`, and `category`. +- Two new RPC functions callable via the Supabase client or REST API. +- No impact on the existing `thoughts` table. Highlights you import later will live in `thoughts` with `source_type = 'readwise'` and `metadata.readwise_book_id` pointing into this table. + +Once the schema is in place, you can install either the backfill recipe, the webhook integration, or both. + +## Troubleshooting + +**Issue: "relation thoughts does not exist"** +Solution: Run the core Open Brain setup first — the `get_book_highlights` function references the `thoughts` table. Follow [docs/01-getting-started.md](../../docs/01-getting-started.md) through at least the database creation step. + +**Issue: `get_book_highlights` returns no rows after importing highlights** +Solution: The function filters on `source_type = 'readwise'` (a column added by the [enhanced-thoughts](../enhanced-thoughts/) schema). If you skipped that schema, your highlights will be in `thoughts` but without the top-level `source_type` column set. Either install `enhanced-thoughts` and run its backfill, or modify your import to set `source_type` explicitly. + +**Issue: `increment_book_highlight_count` runs but counts stay at 0** +Solution: Confirm the book row exists in `readwise_books` before the first highlight arrives. The readwise-capture Edge Function handles this automatically via a write-through cache lookup; if you're calling the RPC manually, you'll need to `INSERT` the book row first. diff --git a/schemas/readwise-books/metadata.json b/schemas/readwise-books/metadata.json new file mode 100644 index 000000000..1154dcb90 --- /dev/null +++ b/schemas/readwise-books/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Readwise Books", + "description": "Side-table cache of Readwise book-level metadata (title, author, cover, category) plus RPCs for in-order highlight retrieval. Required by the readwise-capture integration and readwise-import recipe.", + "category": "schemas", + "author": { + "name": "Mark Lavercombe", + "github": "mlava" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": [], + "tools": [] + }, + "tags": ["readwise", "books", "schema", "cache", "highlights"], + "difficulty": "beginner", + "estimated_time": "5 minutes", + "created": "2026-04-24", + "updated": "2026-04-24" +} diff --git a/schemas/readwise-books/schema.sql b/schemas/readwise-books/schema.sql new file mode 100644 index 000000000..6bacc7cdd --- /dev/null +++ b/schemas/readwise-books/schema.sql @@ -0,0 +1,107 @@ +-- Readwise Books Cache +-- Side-table for book-level metadata so highlights stored in `thoughts` +-- can reference a book_id without denormalising title/author into every +-- highlight row. Populated by the readwise-capture integration and the +-- readwise-import recipe. +-- +-- Safe to run multiple times (fully idempotent). + +-- ============================================================ +-- 1. TABLE +-- ============================================================ + +CREATE TABLE IF NOT EXISTS readwise_books ( + book_id BIGINT PRIMARY KEY, -- Readwise's user_book_id + title TEXT NOT NULL, + author TEXT, + category TEXT, -- books | articles | podcasts | tweets | supplementals + source TEXT, -- kindle | reader | instapaper | apple_books | hypothesis | ... + source_url TEXT, + cover_image_url TEXT, + num_highlights INTEGER DEFAULT 0, + tags JSONB DEFAULT '[]'::jsonb, + last_highlight_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now(), + updated_at TIMESTAMPTZ DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_readwise_books_title + ON readwise_books (title); +CREATE INDEX IF NOT EXISTS idx_readwise_books_author + ON readwise_books (author); +CREATE INDEX IF NOT EXISTS idx_readwise_books_category + ON readwise_books (category); + +-- ============================================================ +-- 2. GET HIGHLIGHTS FOR A BOOK +-- Returns highlights in the order you encountered them in the +-- source (by `location`), with `highlighted_at` as a tiebreaker. +-- NULLIF guards against empty-string metadata values from +-- older imports; empty string would otherwise crash ::bigint. +-- ============================================================ + +CREATE OR REPLACE FUNCTION get_book_highlights( + p_book_id BIGINT, + p_limit INTEGER DEFAULT 500 +) +RETURNS TABLE ( + id UUID, + content TEXT, + note TEXT, + location BIGINT, + location_type TEXT, + highlighted_at TIMESTAMPTZ, + metadata JSONB +) +LANGUAGE sql STABLE +AS $$ + SELECT + t.id, + t.content, + t.metadata->>'note' AS note, + NULLIF(t.metadata->>'location', '')::bigint AS location, + t.metadata->>'location_type' AS location_type, + (t.metadata->>'highlighted_at')::timestamptz AS highlighted_at, + t.metadata + FROM thoughts t + WHERE t.source_type = 'readwise' + AND (t.metadata->>'readwise_book_id')::bigint = p_book_id + ORDER BY + NULLIF(t.metadata->>'location', '')::bigint NULLS LAST, + (t.metadata->>'highlighted_at')::timestamptz NULLS LAST + LIMIT p_limit; +$$; + +GRANT EXECUTE ON FUNCTION get_book_highlights(BIGINT, INTEGER) + TO authenticated, anon, service_role; + +-- ============================================================ +-- 3. INCREMENT HIGHLIGHT COUNT +-- Called by the readwise-capture Edge Function on each new +-- highlight insert. Keeps num_highlights and last_highlight_at +-- fresh without requiring a COUNT over the thoughts table. +-- ============================================================ + +CREATE OR REPLACE FUNCTION increment_book_highlight_count( + p_book_id BIGINT, + p_highlighted_at TIMESTAMPTZ DEFAULT NULL +) +RETURNS VOID +LANGUAGE sql +AS $$ + UPDATE readwise_books + SET + num_highlights = num_highlights + 1, + last_highlight_at = GREATEST( + COALESCE(last_highlight_at, '-infinity'::timestamptz), + COALESCE(p_highlighted_at, '-infinity'::timestamptz) + ), + updated_at = now() + WHERE book_id = p_book_id; +$$; + +GRANT EXECUTE ON FUNCTION increment_book_highlight_count(BIGINT, TIMESTAMPTZ) + TO authenticated, anon, service_role; + +-- Reload PostgREST schema cache so the new RPCs are immediately callable. +NOTIFY pgrst, 'reload schema'; From 5c161aa3ed07328d1461e88bfffc12fe5712e597 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 07:44:27 +1000 Subject: [PATCH 049/125] [integrations] Add readwise-capture webhook receiver Supabase Edge Function that subscribes to readwise.highlight.created webhooks and inserts each highlight as a thought with source_type='readwise'. Every highlight Readwise aggregates -- from Kindle, Apple Books, Reader, Instapaper, Hypothesis, Airr/Snipd podcasts, and the Readwise OCR app -- flows through the same event and lands in one searchable feed. Design choices: - Auth via body.secret echoed by Readwise, compared against READWISE_WEBHOOK_SECRET. - Other event types (Reader document events, tag updates) are ignored with a 200 response so subscribing broader doesn't break us. - Dedup query filters on source_type first to use the index from enhanced-thoughts before the metadata JSONB contains check. - Book metadata resolved via a write-through cache in readwise_books so repeat highlights from the same book don't re-hit the Readwise API. - No per-highlight LLM metadata extraction; book title/author plus the highlight text are the metadata. Topic enrichment is left to the existing thought-enrichment recipe as a separate pass. Depends on the readwise-books schema. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/readwise-capture/README.md | 187 +++++++++++++++++++ integrations/readwise-capture/index.ts | 193 ++++++++++++++++++++ integrations/readwise-capture/metadata.json | 20 ++ 3 files changed, 400 insertions(+) create mode 100644 integrations/readwise-capture/README.md create mode 100644 integrations/readwise-capture/index.ts create mode 100644 integrations/readwise-capture/metadata.json diff --git a/integrations/readwise-capture/README.md b/integrations/readwise-capture/README.md new file mode 100644 index 000000000..d6f2a8cfe --- /dev/null +++ b/integrations/readwise-capture/README.md @@ -0,0 +1,187 @@ +# Readwise Capture Integration + +## What It Does + +Receives Readwise highlight webhooks and stores each highlight as a thought in your Open Brain — automatically embedded, classified as a reference, and tagged with book title, author, and location. Works for highlights made anywhere Readwise aggregates from: Kindle, Apple Books, Reader, Instapaper, Hypothesis, Airr/Snipd podcasts, and physical books via the Readwise OCR app. + +## Prerequisites + +- A working Open Brain setup (follow the [Getting Started guide](../../docs/01-getting-started.md) through Step 7 — you need the Supabase database, OpenRouter API key, and Supabase CLI installed) +- The [readwise-books schema](../../schemas/readwise-books/) applied to your Supabase project +- Recommended: the [enhanced-thoughts schema](../../schemas/enhanced-thoughts/) applied, so highlights carry the top-level `source_type = 'readwise'` column that the `get_book_highlights` RPC filters on +- A Readwise account with webhooks enabled (any paid plan; Readwise Free doesn't expose webhooks) + +## Cost + +Readwise webhooks are free with any paid plan. The Edge Function uses the same OpenRouter credits from your main Open Brain setup — embeddings cost ~$0.02 per million tokens, so a typical highlight (~30 tokens) is a fraction of a cent. For 20 new highlights a day, expect roughly $0.01–0.05/month in API costs. + +For one-shot backfill of your existing library (everything you've ever highlighted), use the [readwise-import recipe](../../recipes/readwise-import/) — the webhook only fires on highlights created after it's registered. + +--- + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +READWISE CAPTURE -- CREDENTIAL TRACKER +-------------------------------------- + +FROM YOUR OPEN BRAIN SETUP + OpenRouter API key: ____________ + Supabase project ref: ____________ + +READWISE + Access token: ____________ (from readwise.io/access_token) + Webhook secret: ____________ (generate your own random string) + +GENERATED DURING SETUP + Edge Function URL: https://____________.supabase.co/functions/v1/readwise-capture + Webhook ID: ____________ (from Readwise webhooks UI) + +-------------------------------------- +``` + +--- + +## Step 1: Get Your Readwise Access Token + +1. Go to [readwise.io/access_token](https://readwise.io/access_token) while signed in +2. Copy the token — save it as the Access Token in the tracker above + +This is a long-lived personal token. It's used only for book-metadata lookups (one call per new book, not per highlight). If you rotate it later, update the `READWISE_ACCESS_TOKEN` Supabase secret. + +--- + +## Step 2: Generate a Webhook Secret + +Readwise authenticates webhooks by echoing back a secret you choose. Generate a random string — anything hard to guess will do. On macOS or Linux: + +```bash +openssl rand -base64 32 +``` + +Save the output as the Webhook Secret in the tracker. You'll paste the same value into both the Readwise webhook config and your Supabase secrets. + +--- + +## Step 3: Deploy the Edge Function + +### Verify Supabase CLI + +```bash +supabase --version +``` + +If that fails, go back to the [Getting Started guide](../../docs/01-getting-started.md) Step 7 and install the CLI first. + +### Log In and Link (if not already done) + +```bash +supabase login +supabase link --project-ref YOUR_PROJECT_REF +``` + +Replace `YOUR_PROJECT_REF` with the value from your Supabase dashboard URL: `supabase.com/dashboard/project/THIS_PART`. + +### Create the Function + +```bash +supabase functions new readwise-capture +``` + +Open `supabase/functions/readwise-capture/index.ts` and replace its entire contents with the contents of [index.ts](./index.ts) from this folder. + +### Set Your Secrets + +```bash +supabase secrets set OPENROUTER_API_KEY=your-openrouter-key +supabase secrets set READWISE_ACCESS_TOKEN=your-readwise-access-token +supabase secrets set READWISE_WEBHOOK_SECRET=the-secret-you-generated-in-step-2 +``` + +> `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are automatically available inside Edge Functions — you don't need to set them. + +### Deploy + +```bash +supabase functions deploy readwise-capture --no-verify-jwt +``` + +Copy the Edge Function URL from the output — it looks like `https://YOUR_REF.supabase.co/functions/v1/readwise-capture`. Save it in the tracker for Step 4. + +--- + +## Step 4: Register the Webhook in Readwise + +1. Go to [readwise.io/integrations](https://readwise.io/integrations) and find the **Webhooks** section (or the [Readwise webhooks docs](https://docs.readwise.io/readwise/docs/webhooks) for the latest UI) +2. Click **Add Webhook** +3. Fill in: + - **URL**: your Edge Function URL from Step 3 + - **Secret**: the webhook secret you generated in Step 2 (must exactly match `READWISE_WEBHOOK_SECRET`) + - **Events**: select only `readwise.highlight.created` +4. Save the webhook + +> **Subscribe to `readwise.highlight.created` only.** The Edge Function will ignore other event types with a 200 "ignored" response, but subscribing to everything wastes Readwise's webhook budget and costs you nothing in return. The only events currently handled are highlight-created events; Reader document events are deliberately out of scope (see the [design discussion](../../recipes/readwise-import/README.md#what-this-captures-and-what-it-doesnt) in the sister recipe). + +--- + +## Step 5: Test It + +Make a new highlight in any Readwise-connected app — highlight a sentence in Kindle, shortlist an article in Reader and highlight a line, whatever's handy. + +Within 10–30 seconds (Readwise batches some sources), open Supabase Dashboard → Table Editor → `thoughts`. You should see one new row with: + +- `content` = the highlight text +- `source_type = 'readwise'` +- `type = 'reference'` +- `metadata` containing `readwise_highlight_id`, `readwise_book_id`, `book_title`, `book_author`, and the full set of Readwise fields + +Check the `readwise_books` table too — the book should appear there with title, author, category, and `num_highlights` incremented. + +--- + +## Expected Outcome + +Every new highlight you make across every Readwise-connected source automatically lands in your Open Brain as an embedded, searchable thought — joined to a book record with title, author, and cover image that any MCP-connected AI can query. + +--- + +## Troubleshooting + +### Readwise shows the webhook as failing + +Check the Edge Function logs: Supabase Dashboard → Edge Functions → `readwise-capture` → Logs. The most common causes: + +- `401 unauthorized` — the `secret` in the webhook payload doesn't match `READWISE_WEBHOOK_SECRET`. Rotate one to match the other and redeploy. +- `500 error` — an OpenRouter or Supabase call failed. Check the log for the specific error. + +### Highlights are being captured but without book title/author + +`book_title` / `book_author` land as `null` when the Readwise book-lookup call fails. Most common cause is the access token: verify `READWISE_ACCESS_TOKEN` matches the value at [readwise.io/access_token](https://readwise.io/access_token) and that the token hasn't been rotated. + +You can fix existing rows by calling `GET /api/v2/books/{book_id}/` manually and backfilling the metadata — or just let the next highlight from that book re-trigger the lookup (the write-through cache will upsert the book row, but existing thought rows with null book_title won't be updated). + +### Duplicate highlights in the database + +The Edge Function deduplicates on `metadata->>readwise_highlight_id` before inserting. If you see duplicates, confirm you're on the latest version of `index.ts` and have redeployed. Also check that your Readwise webhook isn't configured twice pointing at the same URL. + +### `increment_book_highlight_count` RPC not found + +You haven't applied the [readwise-books schema](../../schemas/readwise-books/) yet, or the PostgREST schema cache hasn't reloaded. Run the schema's `NOTIFY pgrst, 'reload schema'` statement manually, or wait a minute and retry. + +### Highlights are arriving out of order + +That's expected for the webhook path — Readwise batches highlights in some sources (Kindle sync runs periodically, podcasts on finish, etc.). If you want guaranteed-complete history, run the [readwise-import recipe](../../recipes/readwise-import/) — it pages through the full export endpoint and is idempotent, so it's safe to run on a schedule alongside the webhook. + +--- + +## What You Just Built + +A live feed from every Readwise-connected source directly into your Open Brain. The moment you highlight a line in Kindle, shortlist-and-annotate in Reader, or snip a podcast moment in Airr, that highlight is in your vector store — joined to a book record, tagged with location, and searchable by any AI tool connected to your MCP server. + +The webhook only catches new highlights. For everything you've ever highlighted before setting this up, pair it with the [readwise-import recipe](../../recipes/readwise-import/). + +--- + +*Built by Mark Lavercombe — part of the [Open Brain project](https://github.com/NateBJones-Projects/OB1)* diff --git a/integrations/readwise-capture/index.ts b/integrations/readwise-capture/index.ts new file mode 100644 index 000000000..c34d5e749 --- /dev/null +++ b/integrations/readwise-capture/index.ts @@ -0,0 +1,193 @@ +// readwise-capture / index.ts +// +// Supabase Edge Function that receives Readwise highlight webhooks, +// embeds the highlight text, and inserts it into the `thoughts` table +// with source_type='readwise'. Uses a write-through cache in the +// `readwise_books` table so highlights can carry book title/author +// without one Readwise API call per highlight. + +import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; + +const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; +const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; +const OPENROUTER_API_KEY = Deno.env.get("OPENROUTER_API_KEY")!; +const READWISE_ACCESS_TOKEN = Deno.env.get("READWISE_ACCESS_TOKEN")!; +const READWISE_WEBHOOK_SECRET = Deno.env.get("READWISE_WEBHOOK_SECRET")!; + +const OPENROUTER_BASE = "https://openrouter.ai/api/v1"; +const READWISE_BASE = "https://readwise.io/api/v2"; +const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY); + +interface HighlightEvent { + id: number; + text: string; + note: string; + location: number | null; + location_type: string; + highlighted_at: string | null; + url: string | null; + color: string; + updated: string; + book_id: number; + tags: Array<{ id: number; name: string }>; + event_type: string; + secret: string; +} + +interface ReadwiseBook { + id: number; + title: string; + author: string | null; + category: string; + source: string | null; + source_url: string | null; + cover_image_url: string | null; + num_highlights: number; + last_highlight_at: string | null; + tags: Array<{ id: number; name: string }>; +} + +async function getEmbedding(text: string): Promise { + const r = await fetch(`${OPENROUTER_BASE}/embeddings`, { + method: "POST", + headers: { + "Authorization": `Bearer ${OPENROUTER_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ model: "openai/text-embedding-3-small", input: text }), + }); + const d = await r.json(); + return d.data[0].embedding; +} + +async function fetchBook(bookId: number): Promise { + const r = await fetch(`${READWISE_BASE}/books/${bookId}/`, { + headers: { "Authorization": `Token ${READWISE_ACCESS_TOKEN}` }, + }); + if (!r.ok) { + console.error(`Readwise book fetch failed (${bookId}): ${r.status}`); + return null; + } + return await r.json(); +} + +async function resolveBook(bookId: number): Promise { + const { data: cached } = await supabase + .from("readwise_books") + .select("book_id, title, author, category, source") + .eq("book_id", bookId) + .maybeSingle(); + + if (cached) { + return { + id: cached.book_id, + title: cached.title, + author: cached.author, + category: cached.category, + source: cached.source, + source_url: null, + cover_image_url: null, + num_highlights: 0, + last_highlight_at: null, + tags: [], + }; + } + + const book = await fetchBook(bookId); + if (!book) return null; + + await supabase.from("readwise_books").upsert({ + book_id: book.id, + title: book.title, + author: book.author, + category: book.category, + source: book.source, + source_url: book.source_url, + cover_image_url: book.cover_image_url, + num_highlights: book.num_highlights, + last_highlight_at: book.last_highlight_at, + tags: book.tags ?? [], + updated_at: new Date().toISOString(), + }); + + return book; +} + +Deno.serve(async (req: Request): Promise => { + try { + const body = await req.json(); + + // Readwise echoes the webhook secret in the payload; reject anything + // that doesn't match our configured value. + if (body.secret !== READWISE_WEBHOOK_SECRET) { + return new Response("unauthorized", { status: 401 }); + } + + // This function only handles highlight-created events. Any other + // event type (Reader document events, tag updates, etc.) is ignored + // so subscribing to more events on the Readwise side won't break us. + if (body.event_type !== "readwise.highlight.created") { + return new Response("ignored", { status: 200 }); + } + + const event = body as HighlightEvent; + + // Deduplicate: if Readwise retries the webhook we mustn't create + // two thoughts. Filter on source_type first so the query uses the + // idx_thoughts_source_type index from enhanced-thoughts. + const { data: existing } = await supabase + .from("thoughts") + .select("id") + .eq("source_type", "readwise") + .contains("metadata", { readwise_highlight_id: event.id }) + .limit(1); + if (existing && existing.length > 0) { + return new Response("duplicate", { status: 200 }); + } + + const book = await resolveBook(event.book_id); + + const noteSuffix = event.note ? `\n\n— ${event.note}` : ""; + const content = `${event.text}${noteSuffix}`; + const embedding = await getEmbedding(content); + + const { error } = await supabase.from("thoughts").insert({ + content, + embedding, + source_type: "readwise", + type: "reference", + metadata: { + source: "readwise", + readwise_highlight_id: event.id, + readwise_book_id: event.book_id, + book_title: book?.title ?? null, + book_author: book?.author ?? null, + book_category: book?.category ?? null, + highlighted_at: event.highlighted_at, + note: event.note, + location: event.location, + location_type: event.location_type, + color: event.color, + url: event.url, + tags: event.tags?.map((t) => t.name) ?? [], + }, + }); + + if (error) { + console.error("Supabase insert error:", error); + return new Response("error", { status: 500 }); + } + + if (book) { + await supabase.rpc("increment_book_highlight_count", { + p_book_id: event.book_id, + p_highlighted_at: event.highlighted_at, + }); + } + + return new Response("ok", { status: 200 }); + } catch (err) { + console.error("Function error:", err); + return new Response("error", { status: 500 }); + } +}); diff --git a/integrations/readwise-capture/metadata.json b/integrations/readwise-capture/metadata.json new file mode 100644 index 000000000..3a7f9be69 --- /dev/null +++ b/integrations/readwise-capture/metadata.json @@ -0,0 +1,20 @@ +{ + "name": "Readwise Capture", + "description": "Receive Readwise highlight webhooks and store them as thoughts. Covers highlights from Kindle, Apple Books, Reader, Instapaper, Hypothesis, podcasts, and physical books in a single feed.", + "category": "integrations", + "author": { + "name": "Mark Lavercombe", + "github": "mlava" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Readwise"], + "tools": ["Supabase CLI"] + }, + "tags": ["readwise", "highlights", "capture", "reading", "webhook"], + "difficulty": "beginner", + "estimated_time": "20 minutes", + "created": "2026-04-24", + "updated": "2026-04-24" +} From 03602ad76f32771e637551956fedeadf53cac652 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 07:54:50 +1000 Subject: [PATCH 050/125] [recipes] Add readwise-import backfill recipe Python script that pages through Readwise's /api/v2/export/ endpoint, upserts each book into readwise_books, batch-embeds highlight text via OpenRouter, and inserts each highlight into thoughts with source_type='readwise'. Idempotent on re-run via readwise_highlight_id dedup. Design choices: - Uses /export/ (grouped books with nested highlights) rather than /highlights/ so no separate book lookup per highlight. - Batches embeddings 100 at a time; OpenAI accepts up to 2048 per call but 100 keeps latency reasonable and runs ~20x faster than serial. - --dry-run and --limit for first-run sanity checks before embedding thousands of rows. - Periodic heartbeat every 500 highlights so long runs don't look hung, independent of --verbose which prints per-book. - Respects Retry-After on 429s (Readwise caps /export/ at 20 req/min). Captures highlights only -- not Reader reading history. Reader documents fire through readwise.reader.* webhooks which this recipe and the matching readwise-capture integration deliberately skip. That split is documented in both READMEs. Depends on the readwise-books schema. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/readwise-import/README.md | 198 ++++++++++++++ recipes/readwise-import/import-readwise.py | 302 +++++++++++++++++++++ recipes/readwise-import/metadata.json | 21 ++ recipes/readwise-import/requirements.txt | 2 + 4 files changed, 523 insertions(+) create mode 100644 recipes/readwise-import/README.md create mode 100644 recipes/readwise-import/import-readwise.py create mode 100644 recipes/readwise-import/metadata.json create mode 100644 recipes/readwise-import/requirements.txt diff --git a/recipes/readwise-import/README.md b/recipes/readwise-import/README.md new file mode 100644 index 000000000..4902e7f07 --- /dev/null +++ b/recipes/readwise-import/README.md @@ -0,0 +1,198 @@ +# Readwise Import + +## What It Does + +One-shot backfill of your Readwise highlight history into Open Brain. The script pages through Readwise's `/api/v2/export/` endpoint, upserts each book into the `readwise_books` cache table, batch-embeds highlight text via OpenRouter, and inserts each highlight as a thought with `source_type = 'readwise'`. + +Idempotent: re-running it after new highlights arrive will skip everything already present (dedup by `readwise_highlight_id`) and import only the delta. + +Pair it with the [readwise-capture integration](../../integrations/readwise-capture/) to keep things live after the initial backfill — this recipe handles history, the webhook handles the future. + +## What this captures and what it doesn't + +**Captured:** every highlight across every Readwise-connected source — Kindle, Apple Books, Reader, Instapaper, Hypothesis, Airr/Snipd podcasts, Readwise OCR physical-book highlights, and anything else Readwise aggregates. All of them flow through the same export endpoint. + +**Deliberately not captured:** Reader reading history itself — the list of articles, emails, and RSS items you've read but not highlighted. Reader's webhook and API support it, but "I read this article" is a low-signal data point that clutters search. If you cared enough about a passage to highlight it, it's here. Everything else stays in Reader where it belongs. + +## Prerequisites + +- Working Open Brain setup ([guide](../../docs/01-getting-started.md)) +- The [readwise-books schema](../../schemas/readwise-books/) applied to your Supabase project +- Recommended: the [enhanced-thoughts schema](../../schemas/enhanced-thoughts/) applied, so the idempotency check on re-runs hits the `source_type` index +- A Readwise account with the access token endpoint enabled (any plan; no paid requirement for this recipe) +- Python 3.10 or newer + +## Cost + +At ~$0.02 per million embedding tokens and an average highlight of ~30 tokens, costs are trivial: + +| Library size | Embedding cost | Wall-clock time | +|---|---|---| +| 1,000 highlights | ~$0.001 | <1 min | +| 10,000 highlights | ~$0.006 | ~5 min | +| 50,000 highlights | ~$0.03 | ~20 min | + +No Readwise API cost; the export endpoint is throttled at 20 req/min, which is generous at `pageSize=1000`. + +--- + +## Credential Tracker + +Copy this block into a text editor and fill it in as you go. + +```text +READWISE IMPORT -- CREDENTIAL TRACKER +-------------------------------------- + +FROM YOUR OPEN BRAIN SETUP + Supabase project URL: ____________ + Service role key: ____________ + OpenRouter API key: ____________ + +READWISE + Access token: ____________ (https://readwise.io/access_token) + +-------------------------------------- +``` + +--- + +## Step 1: Install Python Dependencies + +From this folder: + +```bash +pip install -r requirements.txt +``` + +This installs the `requests` and `supabase` Python clients. Use a virtualenv if you don't want them installed globally. + +--- + +## Step 2: Check the Size of Your Library + +A sanity check before running. Paste this, replacing the token: + +```bash +TOKEN=your-readwise-access-token + +echo "Highlights: $(curl -s 'https://readwise.io/api/v2/highlights/?page_size=1' \ + -H "Authorization: Token $TOKEN" | jq '.count')" +echo "Books: $(curl -s 'https://readwise.io/api/v2/books/?page_size=1' \ + -H "Authorization: Token $TOKEN" | jq '.count')" +``` + +This tells you roughly how many rows the backfill will create. Useful to estimate runtime and spot-check the token works. + +--- + +## Step 3: Export the Required Environment Variables + +```bash +export READWISE_ACCESS_TOKEN=your-readwise-token +export SUPABASE_URL=https://YOUR_REF.supabase.co +export SUPABASE_SERVICE_ROLE_KEY=your-service-role-key +export OPENROUTER_API_KEY=your-openrouter-key +``` + +> Use the **service role** key, not the anon key. The backfill writes directly to `thoughts` and `readwise_books`, which is only possible with a role that bypasses RLS. + +--- + +## Step 4: Dry Run + +Always do a dry run first to confirm your credentials and the expected book count: + +```bash +python import-readwise.py --dry-run --limit 50 --verbose +``` + +Expected output is a list of books scanned and a summary line showing what would have been inserted. If you see authentication errors or zero books, fix those before the real run. + +--- + +## Step 5: Full Import + +```bash +python import-readwise.py --verbose +``` + +For large libraries this will take a few minutes. A progress heartbeat prints every 500 highlights so you can see it hasn't stalled: + +``` + ... 500 highlights processed (48/s, 23 books) + ... 1000 highlights processed (51/s, 47 books) +``` + +The script is safe to interrupt and resume — on re-run, already-imported highlights are skipped via `readwise_highlight_id`. + +### Incremental re-runs + +After the first import, you can run with `--updated-after` to pull only what's changed since a given date: + +```bash +python import-readwise.py --updated-after 2026-01-01 --verbose +``` + +This belt-and-braces complement to the webhook is handy if you want periodic reconciliation (e.g., daily cron) even with the live integration installed. + +--- + +## Expected Outcome + +After a successful run: + +- Every book you've ever highlighted in Readwise has a row in `readwise_books` with title, author, category, source, cover image URL, and `num_highlights`. +- Every highlight is a row in `thoughts` with: + - `content` = the highlight text (plus your note, if any, after an em-dash) + - `source_type = 'readwise'` + - `type = 'reference'` + - `metadata` containing `readwise_highlight_id`, `readwise_book_id`, `book_title`, `book_author`, `highlighted_at`, `location`, `location_type`, `color`, `url`, and `tags` +- Every highlight has a 1536-dim embedding, so semantic search in Open Brain immediately surfaces them alongside your other thoughts. + +From any MCP-connected AI you can now ask things like: +- "What have I highlighted about stoicism?" +- "Show me the highlights from Antifragile in reading order" (uses the `get_book_highlights` RPC from [readwise-books](../../schemas/readwise-books/)) +- "What books have I highlighted most in the last year?" + +--- + +## Troubleshooting + +### `KeyError: READWISE_ACCESS_TOKEN` + +You didn't export one of the required env vars. All four are required. The script errors before any network call so you don't partially import with bad credentials. + +### Readwise returns 401 + +Your access token is wrong or rotated. Visit [readwise.io/access_token](https://readwise.io/access_token) and copy the current value. + +### Supabase insert fails with `relation "readwise_books" does not exist` + +You haven't applied the [readwise-books schema](../../schemas/readwise-books/) yet. Run `schema.sql` from that folder in your Supabase SQL Editor, then retry. + +### The script was interrupted partway through + +Safe to re-run — idempotency is per-highlight, not per-book. Already-imported highlights are detected via `readwise_highlight_id` and skipped. Books get re-upserted which is harmless (same values). + +### "Rate limited by Readwise; sleeping..." + +The `/api/v2/export/` endpoint is capped at 20 req/min. The script respects the `Retry-After` header and sleeps automatically. For a library of ~10K highlights this shouldn't trigger, but very large libraries (100K+) may hit it once or twice. + +### Some highlights have `location: null` + +Normal — tweets, supplemental highlights, and some podcast snippets don't have a numeric location. The `get_book_highlights` RPC sorts `NULLS LAST` so these still appear, just at the bottom of the list. + +### Highlights were imported but they don't appear in search + +Check that your Open Brain MCP server is connected to the same Supabase project the backfill wrote to. A mismatch between the MCP's configured `SUPABASE_URL` and the one you exported above is the most common cause. + +--- + +## What You Just Built + +Your entire Readwise library is now embedded in Open Brain. Every passage you've ever marked as worth remembering — across every book, article, podcast, and tweet — is searchable by meaning alongside everything else you've captured. Pair with the [readwise-capture integration](../../integrations/readwise-capture/) to make this a permanent feed rather than a one-time import. + +--- + +*Built by Mark Lavercombe — part of the [Open Brain project](https://github.com/NateBJones-Projects/OB1)* diff --git a/recipes/readwise-import/import-readwise.py b/recipes/readwise-import/import-readwise.py new file mode 100644 index 000000000..a6cbf8140 --- /dev/null +++ b/recipes/readwise-import/import-readwise.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +""" +import-readwise.py -- Backfill your Readwise highlight history into Open Brain. + +Pages through /api/v2/export/ (the incremental export endpoint), +upserts each book into readwise_books, batch-embeds highlight text, +and inserts into `thoughts` with source_type='readwise'. Idempotent +on re-run: already-imported highlights are skipped by their +readwise_highlight_id. + +Pair with the readwise-capture integration to keep things live after +the one-shot backfill. This script catches everything historical; the +webhook catches everything new. + +Usage: + python import-readwise.py + python import-readwise.py --updated-after 2025-01-01 + python import-readwise.py --dry-run --limit 10 --verbose + +Requires environment variables (or a .env file loaded by your shell): + READWISE_ACCESS_TOKEN -- https://readwise.io/access_token + SUPABASE_URL -- your Supabase project URL + SUPABASE_SERVICE_ROLE_KEY -- service role key (bypasses RLS) + OPENROUTER_API_KEY -- for embeddings +""" + +import argparse +import os +import sys +import time +from datetime import datetime, timezone +from typing import Optional + +try: + import requests +except ImportError: + print("Missing dependency: requests") + print("Run: pip install -r requirements.txt") + sys.exit(1) + +try: + from supabase import create_client +except ImportError: + print("Missing dependency: supabase") + print("Run: pip install -r requirements.txt") + sys.exit(1) + + +# -- Config ------------------------------------------------------------------ + +READWISE_BASE = "https://readwise.io/api/v2" +OPENROUTER_BASE = "https://openrouter.ai/api/v1" +EMBEDDING_MODEL = "openai/text-embedding-3-small" +EMBEDDING_BATCH_SIZE = 100 # OpenAI accepts up to 2048; 100 balances latency and throughput +READWISE_PAGE_SIZE = 1000 # Export endpoint max +PROGRESS_EVERY = 500 # Print a heartbeat every N highlights + + +# -- Readwise ---------------------------------------------------------------- + + +def fetch_export_page( + token: str, updated_after: Optional[str], cursor: Optional[str] +) -> dict: + """Fetch a single page from /api/v2/export/, retrying on 429 rate limits.""" + params = {"pageSize": READWISE_PAGE_SIZE} + if updated_after: + params["updatedAfter"] = updated_after + if cursor: + params["pageCursor"] = cursor + + for attempt in range(5): + r = requests.get( + f"{READWISE_BASE}/export/", + params=params, + headers={"Authorization": f"Token {token}"}, + timeout=60, + ) + if r.status_code == 429: + wait = int(r.headers.get("Retry-After", 60)) + print(f"Rate limited by Readwise; sleeping {wait}s...", flush=True) + time.sleep(wait) + continue + r.raise_for_status() + return r.json() + + raise RuntimeError("Readwise export: too many retries") + + +# -- Embeddings -------------------------------------------------------------- + + +def embed_batch(api_key: str, texts: list[str]) -> list[list[float]]: + """Embed up to 2048 strings in a single OpenRouter call.""" + r = requests.post( + f"{OPENROUTER_BASE}/embeddings", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json={"model": EMBEDDING_MODEL, "input": texts}, + timeout=120, + ) + r.raise_for_status() + return [item["embedding"] for item in r.json()["data"]] + + +# -- Supabase ---------------------------------------------------------------- + + +def upsert_book(supabase, book: dict) -> None: + """Insert or update the readwise_books row for a book.""" + supabase.table("readwise_books").upsert( + { + "book_id": book["user_book_id"], + "title": book["title"], + "author": book.get("author"), + "category": book.get("category"), + "source": book.get("source"), + "source_url": book.get("source_url"), + "cover_image_url": book.get("cover_image_url"), + "num_highlights": book.get("num_highlights", 0), + "last_highlight_at": book.get("last_highlight_at"), + "tags": book.get("book_tags", []), + "updated_at": datetime.now(timezone.utc).isoformat(), + } + ).execute() + + +def already_imported(supabase, highlight_ids: list[int]) -> set[int]: + """Return the subset of highlight IDs already present in `thoughts`.""" + if not highlight_ids: + return set() + # Query each book's highlights in one round-trip. We filter on + # source_type first so the query uses the source_type index + # (from enhanced-thoughts) before the jsonb field extraction. + resp = ( + supabase.table("thoughts") + .select("metadata") + .eq("source_type", "readwise") + .in_( + "metadata->>readwise_highlight_id", + [str(hid) for hid in highlight_ids], + ) + .execute() + ) + return { + int(row["metadata"]["readwise_highlight_id"]) + for row in resp.data + if row.get("metadata", {}).get("readwise_highlight_id") is not None + } + + +def build_thought(highlight: dict, book: dict) -> dict: + """Build the `thoughts` row for a single highlight.""" + text = highlight["text"] + note = highlight.get("note") or "" + content = f"{text}\n\n— {note}" if note else text + + return { + "content": content, + "source_type": "readwise", + "type": "reference", + "metadata": { + "source": "readwise", + "readwise_highlight_id": highlight["id"], + "readwise_book_id": book["user_book_id"], + "book_title": book["title"], + "book_author": book.get("author"), + "book_category": book.get("category"), + "highlighted_at": highlight.get("highlighted_at"), + "note": note, + "location": highlight.get("location"), + "location_type": highlight.get("location_type"), + "color": highlight.get("color"), + "url": highlight.get("url"), + "tags": [t["name"] for t in highlight.get("tags", [])], + }, + } + + +# -- Main -------------------------------------------------------------------- + + +def main() -> None: + p = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + p.add_argument( + "--updated-after", + help="ISO date (e.g. 2025-01-01); only import highlights updated after this", + ) + p.add_argument( + "--dry-run", action="store_true", help="Parse and report, but do not write" + ) + p.add_argument( + "--limit", + type=int, + help="Stop after this many highlights (useful for first-run sanity checks)", + ) + p.add_argument( + "--verbose", + action="store_true", + help="Print a line per book in addition to the periodic heartbeat", + ) + args = p.parse_args() + + try: + token = os.environ["READWISE_ACCESS_TOKEN"] + supabase_url = os.environ["SUPABASE_URL"] + service_key = os.environ["SUPABASE_SERVICE_ROLE_KEY"] + openrouter_key = os.environ["OPENROUTER_API_KEY"] + except KeyError as e: + print(f"Missing required env var: {e.args[0]}") + print( + "Set READWISE_ACCESS_TOKEN, SUPABASE_URL, " + "SUPABASE_SERVICE_ROLE_KEY, and OPENROUTER_API_KEY." + ) + sys.exit(1) + + supabase = create_client(supabase_url, service_key) + + cursor: Optional[str] = None + total_highlights = 0 + total_inserted = 0 + total_skipped = 0 + total_books = 0 + last_progress = 0 + started = time.monotonic() + + while True: + page = fetch_export_page(token, args.updated_after, cursor) + results = page.get("results", []) + + for book in results: + total_books += 1 + if not args.dry_run: + upsert_book(supabase, book) + + highlights = book.get("highlights", []) + if not highlights: + if args.verbose: + print(f"[{book['title'][:50]}] no highlights") + continue + + existing = already_imported(supabase, [h["id"] for h in highlights]) + new_highlights = [h for h in highlights if h["id"] not in existing] + total_skipped += len(existing) + + if args.verbose: + print( + f"[{book['title'][:50]}] " + f"{len(new_highlights)} new, {len(existing)} skipped" + ) + + for i in range(0, len(new_highlights), EMBEDDING_BATCH_SIZE): + batch = new_highlights[i : i + EMBEDDING_BATCH_SIZE] + thoughts = [build_thought(h, book) for h in batch] + texts = [t["content"] for t in thoughts] + + if not args.dry_run: + embeddings = embed_batch(openrouter_key, texts) + for thought, emb in zip(thoughts, embeddings): + thought["embedding"] = emb + supabase.table("thoughts").insert(thoughts).execute() + + total_inserted += len(batch) + total_highlights += len(batch) + + # Heartbeat so long runs don't look hung + if total_highlights - last_progress >= PROGRESS_EVERY: + elapsed = time.monotonic() - started + rate = total_highlights / elapsed if elapsed > 0 else 0 + print( + f" ... {total_highlights} highlights processed " + f"({rate:.0f}/s, {total_books} books)", + flush=True, + ) + last_progress = total_highlights + + if args.limit and total_highlights >= args.limit: + print(f"\nReached --limit {args.limit}; stopping.") + _summary(total_books, total_inserted, total_skipped, args.dry_run) + return + + cursor = page.get("nextPageCursor") + if not cursor: + break + + _summary(total_books, total_inserted, total_skipped, args.dry_run) + + +def _summary(books: int, inserted: int, skipped: int, dry_run: bool) -> None: + prefix = "[DRY RUN] Would have" if dry_run else "" + print() + print(f"{prefix} processed {books} books") + print(f"{prefix} inserted {inserted} highlights") + print(f"{prefix} skipped {skipped} highlights (already present)") + + +if __name__ == "__main__": + main() diff --git a/recipes/readwise-import/metadata.json b/recipes/readwise-import/metadata.json new file mode 100644 index 000000000..9ff65b1bc --- /dev/null +++ b/recipes/readwise-import/metadata.json @@ -0,0 +1,21 @@ +{ + "name": "Readwise Import", + "description": "One-shot backfill of your Readwise highlight history into Open Brain via the /api/v2/export/ endpoint. Pair with the readwise-capture integration to keep it live.", + "category": "recipes", + "author": { + "name": "Mark Lavercombe", + "github": "mlava" + }, + "version": "1.0.0", + "requires": { + "open_brain": true, + "services": ["Readwise"], + "tools": ["Python 3.10+"] + }, + "requires_skills": [], + "tags": ["readwise", "highlights", "import", "backfill", "reading"], + "difficulty": "beginner", + "estimated_time": "15 minutes", + "created": "2026-04-24", + "updated": "2026-04-24" +} diff --git a/recipes/readwise-import/requirements.txt b/recipes/readwise-import/requirements.txt new file mode 100644 index 000000000..f67af0490 --- /dev/null +++ b/recipes/readwise-import/requirements.txt @@ -0,0 +1,2 @@ +requests>=2.31.0 +supabase>=2.0.0 From 7e809cb14b825acf4781bd5e33939862d31c948e Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 08:14:24 +1000 Subject: [PATCH 051/125] [recipes] Add selective backfill flags to readwise-import Expands import-readwise.py with a set of filters so users can run targeted backfills instead of being forced to ingest everything at once: - --book-id ID (repeatable): limit to specific Readwise book_ids - --source NAME (repeatable): limit to sources (kindle, reader, ...) - --category NAME (repeatable): limit to categories (books, articles, podcasts, tweets, supplementals) - --highlighted-after / --highlighted-before: date-range filter on when the highlight was made (the usual mental model) - --updated-before: upper bound on the record update timestamp, pair with the existing --updated-after for reconciliation use cases - --list-books: print a TSV of book_id/count/source/category/title and exit; discovery helper for grabbing --book-id values All new filters are AND-combined and apply client-side after fetching pages from /api/v2/export/. The export endpoint only accepts updatedAfter server-side, so client-side is the only place additional predicates can live. Pagination still happens across the full library, but skipped books are a no-op -- no embedding, no insert. Highlight-level filters exclude highlights where highlighted_at is null (tweets, some podcast snippets) when any --highlighted-* flag is set, since we can't place them in time. Summary output now distinguishes three kinds of not-inserted highlights: books skipped by filter, highlights filtered by date range, and highlights already present. README updated with a "Selective backfill" section that documents every flag, explains highlighted_at vs updated semantics, and includes example invocations. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/readwise-import/README.md | 51 +++++ recipes/readwise-import/import-readwise.py | 254 +++++++++++++++++++-- 2 files changed, 281 insertions(+), 24 deletions(-) diff --git a/recipes/readwise-import/README.md b/recipes/readwise-import/README.md index 4902e7f07..1b5d5e496 100644 --- a/recipes/readwise-import/README.md +++ b/recipes/readwise-import/README.md @@ -138,6 +138,57 @@ This belt-and-braces complement to the webhook is handy if you want periodic rec --- +## Selective backfill + +You don't have to import everything at once. The script accepts several filters so you can run targeted backfills — useful for testing a schema change against one book first, or to gradually ingest a large library by source. + +### Filter reference + +| Flag | What it does | Filter point | +|---|---|---| +| `--updated-after DATE` | Only fetch highlights updated after this ISO date | Readwise API side (cheapest) | +| `--updated-before DATE` | Only keep highlights updated before this | client-side | +| `--highlighted-after DATE` | Only keep highlights made after this | client-side | +| `--highlighted-before DATE` | Only keep highlights made before this | client-side | +| `--book-id ID` (repeatable) | Only this Readwise `user_book_id`. Pass multiple to import several books. | client-side | +| `--source NAME` (repeatable) | Only books from this source (`kindle`, `reader`, `instapaper`, `apple_books`, `hypothesis`, ...) | client-side | +| `--category NAME` (repeatable) | Only books from this category (`books`, `articles`, `podcasts`, `tweets`, `supplementals`) | client-side | +| `--list-books` | Print one TSV row per book (`book_id`, `num_highlights`, `source`, `category`, `title`) and exit | discovery | + +Filters AND together. `--source kindle --category books --highlighted-after 2024-01-01` imports Kindle book highlights made from 2024 onwards. + +### `highlighted_at` vs `updated` + +`--highlighted-after` / `--highlighted-before` filter on when you actually highlighted something. `--updated-before` filters on when Readwise last modified the highlight record (which includes edits to your note months after the fact). The highlight-date pair is the usual mental model; the update-based pair is for reconciliation. + +Highlights with `highlighted_at = null` (tweets, some podcast snippets) are excluded when a `--highlighted-*` filter is set — if we can't place them in time, we can't match a date range. + +### Examples + +```bash +# Discovery: list all books so you can find the IDs you want +python import-readwise.py --list-books | head + +# Just this year's highlights +python import-readwise.py --highlighted-after 2026-01-01 --verbose + +# Only Kindle highlights (skip Reader, Instapaper, tweets) +python import-readwise.py --source kindle --verbose + +# Only books and articles (skip podcasts, tweets, supplementals) +python import-readwise.py --category books --category articles --verbose + +# Re-import one specific book after a schema change +python import-readwise.py --book-id 8237 --verbose + +# Backfill the first half of 2024, from Kindle only, dry-run first +python import-readwise.py --source kindle \ + --highlighted-after 2024-01-01 --highlighted-before 2024-07-01 \ + --dry-run --verbose +``` + +--- + ## Expected Outcome After a successful run: diff --git a/recipes/readwise-import/import-readwise.py b/recipes/readwise-import/import-readwise.py index a6cbf8140..620e97df4 100644 --- a/recipes/readwise-import/import-readwise.py +++ b/recipes/readwise-import/import-readwise.py @@ -14,8 +14,13 @@ Usage: python import-readwise.py - python import-readwise.py --updated-after 2025-01-01 python import-readwise.py --dry-run --limit 10 --verbose + python import-readwise.py --updated-after 2025-01-01 + python import-readwise.py --highlighted-after 2024-06-01 --highlighted-before 2024-12-31 + python import-readwise.py --source kindle --source instapaper + python import-readwise.py --book-id 8237 --book-id 9102 + python import-readwise.py --category books --category articles + python import-readwise.py --list-books Requires environment variables (or a .env file loaded by your shell): READWISE_ACCESS_TOKEN -- https://readwise.io/access_token @@ -56,6 +61,36 @@ PROGRESS_EVERY = 500 # Print a heartbeat every N highlights +# -- Date parsing ------------------------------------------------------------ + + +def parse_iso(value: Optional[str]) -> Optional[datetime]: + """Parse an ISO-8601 string into a tz-aware UTC datetime. Accepts 'Z'.""" + if not value: + return None + s = value.strip() + # Python 3.10's fromisoformat can't handle the 'Z' suffix; normalise. + if s.endswith("Z"): + s = s[:-1] + "+00:00" + try: + dt = datetime.fromisoformat(s) + except ValueError: + return None + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + return dt.astimezone(timezone.utc) + + +def parse_cli_date(value: str) -> datetime: + """Parse a user-supplied date/datetime string for filter flags.""" + dt = parse_iso(value) + if dt is None: + raise argparse.ArgumentTypeError( + f"Invalid date: {value!r} (use YYYY-MM-DD or a full ISO-8601 string)" + ) + return dt + + # -- Readwise ---------------------------------------------------------------- @@ -131,9 +166,6 @@ def already_imported(supabase, highlight_ids: list[int]) -> set[int]: """Return the subset of highlight IDs already present in `thoughts`.""" if not highlight_ids: return set() - # Query each book's highlights in one round-trip. We filter on - # source_type first so the query uses the source_type index - # (from enhanced-thoughts) before the jsonb field extraction. resp = ( supabase.table("thoughts") .select("metadata") @@ -179,6 +211,75 @@ def build_thought(highlight: dict, book: dict) -> dict: } +# -- Filtering --------------------------------------------------------------- + + +def book_matches(book: dict, args) -> bool: + """Apply book-level filters (--book-id, --source, --category).""" + if args.book_id and book.get("user_book_id") not in args.book_id: + return False + if args.source and (book.get("source") or "").lower() not in { + s.lower() for s in args.source + }: + return False + if args.category and (book.get("category") or "").lower() not in { + c.lower() for c in args.category + }: + return False + return True + + +def highlight_matches(highlight: dict, args) -> bool: + """Apply highlight-level filters (date ranges).""" + if args.highlighted_after or args.highlighted_before: + hat = parse_iso(highlight.get("highlighted_at")) + if hat is None: + # Nullable field (tweets, some podcasts). Exclude when a date + # filter is set -- if we can't place it in time, it can't match. + return False + if args.highlighted_after and hat < args.highlighted_after: + return False + if args.highlighted_before and hat > args.highlighted_before: + return False + if args.updated_before: + # Export endpoint returns the per-highlight update timestamp as + # either `updated` or `updated_at` depending on API version. + u = parse_iso(highlight.get("updated") or highlight.get("updated_at")) + if u and u > args.updated_before: + return False + return True + + +# -- List-books mode --------------------------------------------------------- + + +def list_books(token: str, updated_after: Optional[str]) -> None: + """Print a TSV-ish line per book and exit. Discovery helper for --book-id.""" + cursor: Optional[str] = None + total = 0 + print("book_id\tnum_highlights\tsource\tcategory\ttitle") + while True: + page = fetch_export_page(token, updated_after, cursor) + for book in page.get("results", []): + print( + "\t".join( + str(x) + for x in ( + book.get("user_book_id", ""), + book.get("num_highlights", 0), + book.get("source") or "", + book.get("category") or "", + (book.get("title") or "").replace("\t", " "), + ) + ) + ) + total += 1 + cursor = page.get("nextPageCursor") + if not cursor: + break + print(f"\n{total} books total", file=sys.stderr) + + # -- Main -------------------------------------------------------------------- @@ -188,10 +289,51 @@ def main() -> None: ) p.add_argument( "--updated-after", - help="ISO date (e.g. 2025-01-01); only import highlights updated after this", + help="ISO date/datetime; only fetch highlights updated after this (Readwise-side filter)", + ) + p.add_argument( + "--updated-before", + type=parse_cli_date, + help="ISO date/datetime; only keep highlights updated before this (client-side filter)", + ) + p.add_argument( + "--highlighted-after", + type=parse_cli_date, + help="ISO date/datetime; only import highlights made after this", + ) + p.add_argument( + "--highlighted-before", + type=parse_cli_date, + help="ISO date/datetime; only import highlights made before this", + ) + p.add_argument( + "--book-id", + type=int, + action="append", + default=[], + help="Only import highlights from this Readwise book_id. Repeatable.", + ) + p.add_argument( + "--source", + action="append", + default=[], + help="Only books from this source (kindle, reader, instapaper, apple_books, hypothesis, ...). Repeatable.", ) p.add_argument( - "--dry-run", action="store_true", help="Parse and report, but do not write" + "--category", + action="append", + default=[], + help="Only books from this category (books, articles, podcasts, tweets, supplementals). Repeatable.", + ) + p.add_argument( + "--list-books", + action="store_true", + help="Print one TSV row per book (book_id, count, source, category, title) and exit", + ) + p.add_argument( + "--dry-run", + action="store_true", + help="Parse and report, but do not write", ) p.add_argument( "--limit", @@ -205,8 +347,18 @@ def main() -> None: ) args = p.parse_args() + # --list-books only needs the Readwise token. try: token = os.environ["READWISE_ACCESS_TOKEN"] + except KeyError: + print("Missing required env var: READWISE_ACCESS_TOKEN") + sys.exit(1) + + if args.list_books: + list_books(token, args.updated_after) + return + + try: supabase_url = os.environ["SUPABASE_URL"] service_key = os.environ["SUPABASE_SERVICE_ROLE_KEY"] openrouter_key = os.environ["OPENROUTER_API_KEY"] @@ -223,8 +375,10 @@ def main() -> None: cursor: Optional[str] = None total_highlights = 0 total_inserted = 0 - total_skipped = 0 - total_books = 0 + total_skipped_existing = 0 + total_filtered_out = 0 + total_books_seen = 0 + total_books_matched = 0 last_progress = 0 started = time.monotonic() @@ -233,24 +387,54 @@ def main() -> None: results = page.get("results", []) for book in results: - total_books += 1 + total_books_seen += 1 + + if not book_matches(book, args): + if args.verbose: + print( + f"[{(book.get('title') or '')[:50]}] " + f"skipped by book filter" + ) + continue + + total_books_matched += 1 + if not args.dry_run: upsert_book(supabase, book) - highlights = book.get("highlights", []) - if not highlights: + raw_highlights = book.get("highlights", []) + filtered_highlights = [ + h for h in raw_highlights if highlight_matches(h, args) + ] + filtered_count = len(raw_highlights) - len(filtered_highlights) + total_filtered_out += filtered_count + + if not filtered_highlights: if args.verbose: - print(f"[{book['title'][:50]}] no highlights") + print( + f"[{(book.get('title') or '')[:50]}] " + f"no highlights match filters ({filtered_count} filtered)" + ) continue - existing = already_imported(supabase, [h["id"] for h in highlights]) - new_highlights = [h for h in highlights if h["id"] not in existing] - total_skipped += len(existing) + existing = already_imported( + supabase, [h["id"] for h in filtered_highlights] + ) + new_highlights = [ + h for h in filtered_highlights if h["id"] not in existing + ] + total_skipped_existing += len(existing) if args.verbose: + parts = [ + f"{len(new_highlights)} new", + f"{len(existing)} already present", + ] + if filtered_count: + parts.append(f"{filtered_count} filtered out") print( - f"[{book['title'][:50]}] " - f"{len(new_highlights)} new, {len(existing)} skipped" + f"[{(book.get('title') or '')[:50]}] " + + ", ".join(parts) ) for i in range(0, len(new_highlights), EMBEDDING_BATCH_SIZE): @@ -267,35 +451,57 @@ def main() -> None: total_inserted += len(batch) total_highlights += len(batch) - # Heartbeat so long runs don't look hung if total_highlights - last_progress >= PROGRESS_EVERY: elapsed = time.monotonic() - started rate = total_highlights / elapsed if elapsed > 0 else 0 print( f" ... {total_highlights} highlights processed " - f"({rate:.0f}/s, {total_books} books)", + f"({rate:.0f}/s, {total_books_matched} matched books)", flush=True, ) last_progress = total_highlights if args.limit and total_highlights >= args.limit: print(f"\nReached --limit {args.limit}; stopping.") - _summary(total_books, total_inserted, total_skipped, args.dry_run) + _summary( + total_books_seen, + total_books_matched, + total_inserted, + total_skipped_existing, + total_filtered_out, + args.dry_run, + ) return cursor = page.get("nextPageCursor") if not cursor: break - _summary(total_books, total_inserted, total_skipped, args.dry_run) + _summary( + total_books_seen, + total_books_matched, + total_inserted, + total_skipped_existing, + total_filtered_out, + args.dry_run, + ) -def _summary(books: int, inserted: int, skipped: int, dry_run: bool) -> None: +def _summary( + books_seen: int, + books_matched: int, + inserted: int, + skipped_existing: int, + filtered_out: int, + dry_run: bool, +) -> None: prefix = "[DRY RUN] Would have" if dry_run else "" print() - print(f"{prefix} processed {books} books") + print(f"{prefix} seen {books_seen} books ({books_matched} matched filters)") print(f"{prefix} inserted {inserted} highlights") - print(f"{prefix} skipped {skipped} highlights (already present)") + print(f"{prefix} skipped {skipped_existing} already-present highlights") + if filtered_out: + print(f"{prefix} filtered out {filtered_out} highlights by date range") if __name__ == "__main__": From 4319d7687bda68db8db45e4c433b0b354d43ae92 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 08:18:59 +1000 Subject: [PATCH 052/125] [recipes] Fix readwise-import --list-books output Two issues visible on the first real run: 1. BrokenPipeError when piping into `head`. Restore default SIGPIPE handling on Unix so the script exits cleanly instead of raising mid-print. No-op on platforms without SIGPIPE (Windows). 2. num_highlights column showed 0 for every book. The /api/v2/export/ endpoint doesn't populate that field on book records; it's on /api/v2/books/ instead. Since /export/ already returns every highlight nested under its book, count len(book["highlights"]) directly -- authoritative, no extra API calls, and correctly reports the subset count when --updated-after is set. Renamed the column header from "num_highlights" to "highlights" to match the new semantics, and added a trailing total-books + total-highlights line to stderr so the summary doesn't get swallowed by downstream pipes. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/readwise-import/import-readwise.py | 32 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/recipes/readwise-import/import-readwise.py b/recipes/readwise-import/import-readwise.py index 620e97df4..4494833f6 100644 --- a/recipes/readwise-import/import-readwise.py +++ b/recipes/readwise-import/import-readwise.py @@ -31,11 +31,18 @@ import argparse import os +import signal import sys import time from datetime import datetime, timezone from typing import Optional +# Restore default SIGPIPE handling on Unix so piping into `head` or similar +# exits cleanly instead of raising BrokenPipeError mid-print. No-op on +# platforms without SIGPIPE (e.g. Windows). +if hasattr(signal, "SIGPIPE"): + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + try: import requests except ImportError: @@ -254,30 +261,43 @@ def highlight_matches(highlight: dict, args) -> bool: def list_books(token: str, updated_after: Optional[str]) -> None: - """Print a TSV-ish line per book and exit. Discovery helper for --book-id.""" + """Print a TSV-ish line per book and exit. Discovery helper for --book-id. + + The count column reflects highlights present in /export/ for this call: + with no --updated-after, it's the book's total; with --updated-after, + it's the number updated since that date. The export endpoint does not + populate a separate `num_highlights` field, so we count inline. + """ cursor: Optional[str] = None - total = 0 - print("book_id\tnum_highlights\tsource\tcategory\ttitle") + total_books = 0 + total_highlights = 0 + print("book_id\thighlights\tsource\tcategory\ttitle") while True: page = fetch_export_page(token, updated_after, cursor) for book in page.get("results", []): + count = len(book.get("highlights", [])) + total_highlights += count print( "\t".join( str(x) for x in ( book.get("user_book_id", ""), - book.get("num_highlights", 0), + count, book.get("source") or "", book.get("category") or "", (book.get("title") or "").replace("\t", " "), ) ) ) - total += 1 + total_books += 1 cursor = page.get("nextPageCursor") if not cursor: break - print(f"\n{total} books total", file=sys.stderr) + suffix = " (since --updated-after)" if updated_after else "" + print( + f"\n{total_books} books, {total_highlights} highlights{suffix}", + file=sys.stderr, + ) # -- Main -------------------------------------------------------------------- From a312e4fe54165d2bfa6d3afc329182625b44fb87 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 08:27:24 +1000 Subject: [PATCH 053/125] [recipes] Fix readwise_books.num_highlights after backfill upsert_book was trusting book["num_highlights"] from the /export/ response, which isn't populated on that endpoint -- every backfilled book row ended up with num_highlights=0 despite correctly imported highlights in the thoughts table. Cosmetic (no impact on search or get_book_highlights), but breaks any dashboard that reads the count. Count from len(book["highlights"]) in the inline response instead. For full backfills that's the authoritative total. On --updated-after runs the array is a subset, so we skip the field entirely; the upsert's ON CONFLICT DO UPDATE only touches the columns we set, so any count from a previous full run is preserved. Verified on a re-run for book 52113662: upsert with 47 now sets num_highlights=47, matching the count of inserted rows in thoughts. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/readwise-import/import-readwise.py | 47 +++++++++++++--------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/recipes/readwise-import/import-readwise.py b/recipes/readwise-import/import-readwise.py index 4494833f6..347b92b47 100644 --- a/recipes/readwise-import/import-readwise.py +++ b/recipes/readwise-import/import-readwise.py @@ -150,23 +150,30 @@ def embed_batch(api_key: str, texts: list[str]) -> list[list[float]]: # -- Supabase ---------------------------------------------------------------- -def upsert_book(supabase, book: dict) -> None: - """Insert or update the readwise_books row for a book.""" - supabase.table("readwise_books").upsert( - { - "book_id": book["user_book_id"], - "title": book["title"], - "author": book.get("author"), - "category": book.get("category"), - "source": book.get("source"), - "source_url": book.get("source_url"), - "cover_image_url": book.get("cover_image_url"), - "num_highlights": book.get("num_highlights", 0), - "last_highlight_at": book.get("last_highlight_at"), - "tags": book.get("book_tags", []), - "updated_at": datetime.now(timezone.utc).isoformat(), - } - ).execute() +def upsert_book(supabase, book: dict, set_highlight_count: bool) -> None: + """Insert or update the readwise_books row for a book. + + /api/v2/export/ doesn't populate book["num_highlights"], so count the + inline highlights array instead. On full backfills (no --updated-after) + that's the authoritative total. On --updated-after runs the inline array + is a subset -- we omit num_highlights from the payload so ON CONFLICT + DO UPDATE preserves whatever was there from a previous full run. + """ + row = { + "book_id": book["user_book_id"], + "title": book["title"], + "author": book.get("author"), + "category": book.get("category"), + "source": book.get("source"), + "source_url": book.get("source_url"), + "cover_image_url": book.get("cover_image_url"), + "last_highlight_at": book.get("last_highlight_at"), + "tags": book.get("book_tags", []), + "updated_at": datetime.now(timezone.utc).isoformat(), + } + if set_highlight_count: + row["num_highlights"] = len(book.get("highlights", [])) + supabase.table("readwise_books").upsert(row).execute() def already_imported(supabase, highlight_ids: list[int]) -> set[int]: @@ -420,7 +427,11 @@ def main() -> None: total_books_matched += 1 if not args.dry_run: - upsert_book(supabase, book) + upsert_book( + supabase, + book, + set_highlight_count=not args.updated_after, + ) raw_highlights = book.get("highlights", []) filtered_highlights = [ From 52dbfe3359bcb3b11f642885fcc0ea8dec350411 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 08:45:31 +1000 Subject: [PATCH 054/125] [recipes] Handle Supabase statement_timeout during bulk inserts A full backfill hit Postgres error 57014 (canceling statement due to statement timeout) on the thoughts table insert. Supabase's default statement_timeout for the authenticated role is ~8s, which a 100-row batch of 1536-dim vectors can blow past when pgvector does index maintenance on top of the insert itself. Two changes: 1. Decouple insert batch size from embedding batch size. Embedding calls stay at 100 (OpenRouter API, throughput-bound). Inserts drop to 25 (DB-write-bound). For a typical thoughts table with an ivfflat/hnsw index, 25-row inserts land comfortably under the timeout. 2. Add insert_thoughts() that catches APIError with code 57014 and splits the batch in half, retrying each side. Bottoms out at single-row inserts if the split chain continues; raises on any non-timeout error so real failures aren't swallowed. Partial runs are recoverable: rows inserted before the timeout will be skipped on re-run via the readwise_highlight_id dedup check. Co-Authored-By: Claude Opus 4.7 (1M context) --- recipes/readwise-import/import-readwise.py | 34 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/recipes/readwise-import/import-readwise.py b/recipes/readwise-import/import-readwise.py index 347b92b47..6db8c5d07 100644 --- a/recipes/readwise-import/import-readwise.py +++ b/recipes/readwise-import/import-readwise.py @@ -52,6 +52,7 @@ try: from supabase import create_client + from postgrest.exceptions import APIError except ImportError: print("Missing dependency: supabase") print("Run: pip install -r requirements.txt") @@ -63,9 +64,11 @@ READWISE_BASE = "https://readwise.io/api/v2" OPENROUTER_BASE = "https://openrouter.ai/api/v1" EMBEDDING_MODEL = "openai/text-embedding-3-small" -EMBEDDING_BATCH_SIZE = 100 # OpenAI accepts up to 2048; 100 balances latency and throughput +EMBEDDING_BATCH_SIZE = 100 # OpenRouter API calls; throughput-bound, safe to be large +INSERT_BATCH_SIZE = 25 # Supabase inserts; bound by pgvector index maintenance cost READWISE_PAGE_SIZE = 1000 # Export endpoint max PROGRESS_EVERY = 500 # Print a heartbeat every N highlights +STATEMENT_TIMEOUT_CODE = "57014" # Postgres: canceling statement due to statement timeout # -- Date parsing ------------------------------------------------------------ @@ -176,6 +179,28 @@ def upsert_book(supabase, book: dict, set_highlight_count: bool) -> None: supabase.table("readwise_books").upsert(row).execute() +def insert_thoughts(supabase, thoughts: list[dict]) -> None: + """Insert thoughts, splitting the batch recursively on statement timeout. + + Supabase's default statement_timeout for the authenticated role is ~8s. + Inserting many 1536-dim vectors at once occasionally triggers pgvector + index maintenance that blows past that. Splitting the batch in half on + timeout and retrying is almost always enough to get under the limit. + """ + if not thoughts: + return + try: + supabase.table("thoughts").insert(thoughts).execute() + except APIError as e: + code = getattr(e, "code", None) + if code == STATEMENT_TIMEOUT_CODE and len(thoughts) > 1: + mid = len(thoughts) // 2 + insert_thoughts(supabase, thoughts[:mid]) + insert_thoughts(supabase, thoughts[mid:]) + return + raise + + def already_imported(supabase, highlight_ids: list[int]) -> set[int]: """Return the subset of highlight IDs already present in `thoughts`.""" if not highlight_ids: @@ -477,7 +502,12 @@ def main() -> None: embeddings = embed_batch(openrouter_key, texts) for thought, emb in zip(thoughts, embeddings): thought["embedding"] = emb - supabase.table("thoughts").insert(thoughts).execute() + # Split into smaller insert batches so each Supabase + # request stays comfortably under statement_timeout. + for j in range(0, len(thoughts), INSERT_BATCH_SIZE): + insert_thoughts( + supabase, thoughts[j : j + INSERT_BATCH_SIZE] + ) total_inserted += len(batch) total_highlights += len(batch) From 885c06820235aefe3df0bf4f9dcc0d0425e32021 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 09:05:32 +1000 Subject: [PATCH 055/125] [integrations] Handle empty body in readwise-capture Readwise's "Test Webhook" button validates the URL by sending a request with an empty body; req.json() crashed on the "Unexpected end of JSON input" and returned 500, which Readwise's setup flow treats as a failed test and blocks saving the webhook. Three small additions in the handler: - GET requests return 200 "readwise-capture is live" for uptime probes or casual curl-checks. - Empty POST bodies return 200 "ok (empty body)" so the Readwise test validator can pass. Authentic event payloads always include secret + event_type, so nothing downstream breaks from this. - Non-JSON POST bodies return 400 with a logged snippet (truncated to 500 chars) so we can diagnose if Readwise or something else ever sends a malformed payload. Real events still go through the secret check and event-type filter unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/readwise-capture/index.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/integrations/readwise-capture/index.ts b/integrations/readwise-capture/index.ts index c34d5e749..dd3c0c764 100644 --- a/integrations/readwise-capture/index.ts +++ b/integrations/readwise-capture/index.ts @@ -115,7 +115,26 @@ async function resolveBook(bookId: number): Promise { Deno.serve(async (req: Request): Promise => { try { - const body = await req.json(); + // Readwise's "Test Webhook" button hits the URL with an empty body + // (and some infra health checks probe with GET). Respond 200 so the + // webhook setup flow can pass without us pretending to process + // missing data. + if (req.method === "GET") { + return new Response("readwise-capture is live", { status: 200 }); + } + + const bodyText = await req.text(); + if (!bodyText) { + return new Response("ok (empty body)", { status: 200 }); + } + + let body: any; + try { + body = JSON.parse(bodyText); + } catch { + console.error("Invalid JSON body:", bodyText.slice(0, 500)); + return new Response("invalid json", { status: 400 }); + } // Readwise echoes the webhook secret in the payload; reject anything // that doesn't match our configured value. From e0a43ac4fcf88fc89295d243517118a7a648f1f1 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 09:09:10 +1000 Subject: [PATCH 056/125] [integrations] Fix Readwise webhook URL in readwise-capture README The webhook creation page is at readwise.io/webhook, not readwise.io/integrations as previously written. Verified against Readwise's live UI during setup. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/readwise-capture/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/readwise-capture/README.md b/integrations/readwise-capture/README.md index d6f2a8cfe..303b7c317 100644 --- a/integrations/readwise-capture/README.md +++ b/integrations/readwise-capture/README.md @@ -114,7 +114,7 @@ Copy the Edge Function URL from the output — it looks like `https://YOUR_REF.s ## Step 4: Register the Webhook in Readwise -1. Go to [readwise.io/integrations](https://readwise.io/integrations) and find the **Webhooks** section (or the [Readwise webhooks docs](https://docs.readwise.io/readwise/docs/webhooks) for the latest UI) +1. Go to [readwise.io/webhook](https://readwise.io/webhook) (the webhook creation page — see the [Readwise webhooks docs](https://docs.readwise.io/readwise/docs/webhooks) for full reference) 2. Click **Add Webhook** 3. Fill in: - **URL**: your Edge Function URL from Step 3 From 392e5293ca65cae6ee3f26ecc93d80c2a8f9a7aa Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 10:29:41 +1000 Subject: [PATCH 057/125] [integrations] Fix readwise-capture secret flow in README The README instructed users to generate their own webhook secret with openssl rand -base64 32 and paste it into both Readwise and Supabase. In practice Readwise generates the secret itself and does not accept a user-provided value -- the supplied field was silently ignored, so Readwise's secret and ours never matched, and every real event 401'd while the empty-body test passed. Restructured Steps 2-5: - Removed old Step 2 ("Generate a Webhook Secret"). Deferred setting READWISE_WEBHOOK_SECRET until we have the value Readwise generates. - Deploy (now Step 2) only sets OPENROUTER_API_KEY and READWISE_ACCESS_TOKEN. Added a note that real webhooks will 401 until Step 4 configures the secret; the test-webhook button still passes because the empty-body guard runs before the secret check. - Register (now Step 3) drops the "paste a secret" instruction; describes the flow where Readwise shows a generated secret you then copy into the tracker. - New Step 4 stores Readwise's generated secret in Supabase with a note that Edge Function secrets are read at runtime, so no redeploy is needed. - Credential tracker moved "Webhook secret" from an input to a "generated during setup" value. Verified end-to-end: after setting the Readwise-generated value as READWISE_WEBHOOK_SECRET, webhook events start arriving and the secret check passes. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/readwise-capture/README.md | 49 ++++++++++++++----------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/integrations/readwise-capture/README.md b/integrations/readwise-capture/README.md index 303b7c317..280948c87 100644 --- a/integrations/readwise-capture/README.md +++ b/integrations/readwise-capture/README.md @@ -33,10 +33,10 @@ FROM YOUR OPEN BRAIN SETUP READWISE Access token: ____________ (from readwise.io/access_token) - Webhook secret: ____________ (generate your own random string) GENERATED DURING SETUP Edge Function URL: https://____________.supabase.co/functions/v1/readwise-capture + Webhook secret: ____________ (auto-generated by Readwise after you create the webhook) Webhook ID: ____________ (from Readwise webhooks UI) -------------------------------------- @@ -53,19 +53,7 @@ This is a long-lived personal token. It's used only for book-metadata lookups (o --- -## Step 2: Generate a Webhook Secret - -Readwise authenticates webhooks by echoing back a secret you choose. Generate a random string — anything hard to guess will do. On macOS or Linux: - -```bash -openssl rand -base64 32 -``` - -Save the output as the Webhook Secret in the tracker. You'll paste the same value into both the Readwise webhook config and your Supabase secrets. - ---- - -## Step 3: Deploy the Edge Function +## Step 2: Deploy the Edge Function ### Verify Supabase CLI @@ -94,10 +82,11 @@ Open `supabase/functions/readwise-capture/index.ts` and replace its entire conte ### Set Your Secrets +Set two of the three secrets now. The third (`READWISE_WEBHOOK_SECRET`) gets set in Step 4 after Readwise generates it for you — don't set it now, since we don't know the value yet. + ```bash supabase secrets set OPENROUTER_API_KEY=your-openrouter-key supabase secrets set READWISE_ACCESS_TOKEN=your-readwise-access-token -supabase secrets set READWISE_WEBHOOK_SECRET=the-secret-you-generated-in-step-2 ``` > `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are automatically available inside Edge Functions — you don't need to set them. @@ -108,21 +97,37 @@ supabase secrets set READWISE_WEBHOOK_SECRET=the-secret-you-generated-in-step-2 supabase functions deploy readwise-capture --no-verify-jwt ``` -Copy the Edge Function URL from the output — it looks like `https://YOUR_REF.supabase.co/functions/v1/readwise-capture`. Save it in the tracker for Step 4. +Copy the Edge Function URL from the output — it looks like `https://YOUR_REF.supabase.co/functions/v1/readwise-capture`. Save it in the tracker for Step 3. + +At this point the function is deployed but will reject every real webhook with 401 until the webhook secret is configured in Step 4. Readwise's "Test Webhook" button — which sends an empty body — will still pass, because the empty-body check short-circuits before the secret check. --- -## Step 4: Register the Webhook in Readwise +## Step 3: Register the Webhook in Readwise 1. Go to [readwise.io/webhook](https://readwise.io/webhook) (the webhook creation page — see the [Readwise webhooks docs](https://docs.readwise.io/readwise/docs/webhooks) for full reference) 2. Click **Add Webhook** 3. Fill in: - - **URL**: your Edge Function URL from Step 3 - - **Secret**: the webhook secret you generated in Step 2 (must exactly match `READWISE_WEBHOOK_SECRET`) - - **Events**: select only `readwise.highlight.created` -4. Save the webhook + - **URL**: your Edge Function URL from Step 2 + - **Events**: select only `readwise.highlight.created`. Leave every `reader.*` event unchecked — the Edge Function ignores them. +4. Save the webhook. Readwise will generate a secret for you and display it in the webhook entry (under the masked "Secret" field — click **Show** to reveal). +5. **Copy the full secret**. Save it in the tracker; you'll paste it into Supabase in the next step. + +> **Subscribe to `readwise.highlight.created` only.** The Edge Function will ignore other event types with a 200 "ignored" response, but subscribing to everything wastes Readwise's webhook budget and costs you nothing in return. Reader document events are deliberately out of scope (see the [design discussion](../../recipes/readwise-import/README.md#what-this-captures-and-what-it-doesnt) in the sister recipe). + +> **Readwise generates the secret; you cannot set your own.** Every webhook event payload includes this secret, and the Edge Function verifies it matches what's stored in Supabase — so the two must be identical. + +--- + +## Step 4: Store the Webhook Secret in Supabase + +Paste the secret from Step 3 into the Supabase secret store: + +```bash +supabase secrets set READWISE_WEBHOOK_SECRET=the-secret-copied-from-readwise +``` -> **Subscribe to `readwise.highlight.created` only.** The Edge Function will ignore other event types with a 200 "ignored" response, but subscribing to everything wastes Readwise's webhook budget and costs you nothing in return. The only events currently handled are highlight-created events; Reader document events are deliberately out of scope (see the [design discussion](../../recipes/readwise-import/README.md#what-this-captures-and-what-it-doesnt) in the sister recipe). +Edge Functions read secrets at runtime, so no redeploy is needed — the next webhook Readwise sends will be validated against the new secret. --- From 8dea938aa6bdf6d4a3b70e3d77f655f6b49a9746 Mon Sep 17 00:00:00 2001 From: Mark Lavercombe Date: Fri, 24 Apr 2026 10:45:50 +1000 Subject: [PATCH 058/125] [integrations] Correct Readwise plan claim in readwise-capture README Previously stated webhooks required a paid plan ("Readwise Free doesn't expose webhooks"). Verified against a fresh Free-tier account: readwise.io/webhook is available and webhook creation + test succeeds on Free plans. Reworded prerequisites and cost section to reflect this. Co-Authored-By: Claude Opus 4.7 (1M context) --- integrations/readwise-capture/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/readwise-capture/README.md b/integrations/readwise-capture/README.md index 280948c87..37429fb8f 100644 --- a/integrations/readwise-capture/README.md +++ b/integrations/readwise-capture/README.md @@ -9,11 +9,11 @@ Receives Readwise highlight webhooks and stores each highlight as a thought in y - A working Open Brain setup (follow the [Getting Started guide](../../docs/01-getting-started.md) through Step 7 — you need the Supabase database, OpenRouter API key, and Supabase CLI installed) - The [readwise-books schema](../../schemas/readwise-books/) applied to your Supabase project - Recommended: the [enhanced-thoughts schema](../../schemas/enhanced-thoughts/) applied, so highlights carry the top-level `source_type = 'readwise'` column that the `get_book_highlights` RPC filters on -- A Readwise account with webhooks enabled (any paid plan; Readwise Free doesn't expose webhooks) +- A Readwise account (verified working on Free and paid plans — both expose webhook creation at [readwise.io/webhook](https://readwise.io/webhook)) ## Cost -Readwise webhooks are free with any paid plan. The Edge Function uses the same OpenRouter credits from your main Open Brain setup — embeddings cost ~$0.02 per million tokens, so a typical highlight (~30 tokens) is a fraction of a cent. For 20 new highlights a day, expect roughly $0.01–0.05/month in API costs. +Readwise webhooks are free (verified on their Free tier). The Edge Function uses the same OpenRouter credits from your main Open Brain setup — embeddings cost ~$0.02 per million tokens, so a typical highlight (~30 tokens) is a fraction of a cent. For 20 new highlights a day, expect roughly $0.01–0.05/month in API costs. For one-shot backfill of your existing library (everything you've ever highlighted), use the [readwise-import recipe](../../recipes/readwise-import/) — the webhook only fires on highlights created after it's registered. From fee546d5f9bb74a97e196a643c2aad6ca57f624d Mon Sep 17 00:00:00 2001 From: Travis Swicegood Date: Mon, 27 Apr 2026 10:05:31 -0600 Subject: [PATCH 059/125] [dashboards] open-brain-dashboard-next: bump next to 16.2.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch bump within Next 16.x (16.2.1 → 16.2.4, plus matching eslint-config-next). No behavior or API changes — Vercel and any other Node host continue to deploy unchanged. This unblocks downstream tooling that requires next>=16.2.3 (e.g. the @opennextjs/cloudflare adapter) without forcing a major-version upgrade or a --legacy-peer-deps workaround. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../package-lock.json | 114 ++++++++++-------- .../open-brain-dashboard-next/package.json | 4 +- 2 files changed, 65 insertions(+), 53 deletions(-) diff --git a/dashboards/open-brain-dashboard-next/package-lock.json b/dashboards/open-brain-dashboard-next/package-lock.json index 2608fa689..995a4b0cf 100644 --- a/dashboards/open-brain-dashboard-next/package-lock.json +++ b/dashboards/open-brain-dashboard-next/package-lock.json @@ -12,7 +12,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "iron-session": "^8.0.4", - "next": "16.2.1", + "next": "16.2.4", "react": "19.2.4", "react-dom": "19.2.4", "server-only": "^0.0.1" @@ -23,7 +23,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.2.1", + "eslint-config-next": "16.2.4", "tailwindcss": "^4", "typescript": "^5" } @@ -1093,15 +1093,15 @@ } }, "node_modules/@next/env": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.1.tgz", - "integrity": "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.4.tgz", + "integrity": "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz", - "integrity": "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.4.tgz", + "integrity": "sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA==", "dev": true, "license": "MIT", "dependencies": { @@ -1109,9 +1109,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz", - "integrity": "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz", + "integrity": "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==", "cpu": [ "arm64" ], @@ -1125,9 +1125,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz", - "integrity": "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz", + "integrity": "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==", "cpu": [ "x64" ], @@ -1141,12 +1141,15 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz", - "integrity": "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz", + "integrity": "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1157,12 +1160,15 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz", - "integrity": "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz", + "integrity": "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1173,12 +1179,15 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz", - "integrity": "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz", + "integrity": "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1189,12 +1198,15 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz", - "integrity": "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz", + "integrity": "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1205,9 +1217,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz", - "integrity": "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz", + "integrity": "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==", "cpu": [ "arm64" ], @@ -1221,9 +1233,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz", - "integrity": "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz", + "integrity": "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==", "cpu": [ "x64" ], @@ -3178,13 +3190,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.1.tgz", - "integrity": "sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.4.tgz", + "integrity": "sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.1", + "@next/eslint-plugin-next": "16.2.4", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -5076,12 +5088,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz", - "integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.4.tgz", + "integrity": "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==", "license": "MIT", "dependencies": { - "@next/env": "16.2.1", + "@next/env": "16.2.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -5095,14 +5107,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.1", - "@next/swc-darwin-x64": "16.2.1", - "@next/swc-linux-arm64-gnu": "16.2.1", - "@next/swc-linux-arm64-musl": "16.2.1", - "@next/swc-linux-x64-gnu": "16.2.1", - "@next/swc-linux-x64-musl": "16.2.1", - "@next/swc-win32-arm64-msvc": "16.2.1", - "@next/swc-win32-x64-msvc": "16.2.1", + "@next/swc-darwin-arm64": "16.2.4", + "@next/swc-darwin-x64": "16.2.4", + "@next/swc-linux-arm64-gnu": "16.2.4", + "@next/swc-linux-arm64-musl": "16.2.4", + "@next/swc-linux-x64-gnu": "16.2.4", + "@next/swc-linux-x64-musl": "16.2.4", + "@next/swc-win32-arm64-msvc": "16.2.4", + "@next/swc-win32-x64-msvc": "16.2.4", "sharp": "^0.34.5" }, "peerDependencies": { @@ -5420,9 +5432,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { diff --git a/dashboards/open-brain-dashboard-next/package.json b/dashboards/open-brain-dashboard-next/package.json index b369d301b..1f3d4ea82 100644 --- a/dashboards/open-brain-dashboard-next/package.json +++ b/dashboards/open-brain-dashboard-next/package.json @@ -13,7 +13,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "iron-session": "^8.0.4", - "next": "16.2.1", + "next": "16.2.4", "react": "19.2.4", "react-dom": "19.2.4", "server-only": "^0.0.1" @@ -24,7 +24,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.2.1", + "eslint-config-next": "16.2.4", "tailwindcss": "^4", "typescript": "^5" } From ef8cf731e09bce1810a004d77cc8d82d86bdd92a Mon Sep 17 00:00:00 2001 From: Travis Swicegood Date: Mon, 27 Apr 2026 10:07:19 -0600 Subject: [PATCH 060/125] [dashboards] open-brain-dashboard-next: add Cloudflare Workers deploy support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the @opennextjs/cloudflare adapter so the dashboard can deploy to Cloudflare Workers alongside the open-brain-rest gateway, keeping the whole second-brain stack on a single platform. The older @cloudflare/next-on-pages adapter caps at Next 15.5.x and doesn't support this dashboard's Next 16, so OpenNext is the only path that works without a major-version downgrade. Builds on the next 16.2.4 patch bump merged from contrib/tswicegood/dashboard-next-bump (which clears OpenNext's next>=16.2.3 peer dep on its own). Changes: - @opennextjs/cloudflare and wrangler as dev deps. - open-next.config.ts and wrangler.jsonc — the two config files OpenNext needs at the project root. Worker name defaults to ob-dashboard; deployers can rename in wrangler.jsonc. - README: new "Deploy to Cloudflare Workers" subsection alongside the existing Vercel one, including the build-time vs. runtime distinction for NEXT_PUBLIC_API_URL vs. SESSION_SECRET (catching that was the difference between a working deploy and a half- configured one). - .gitignore: exclude .open-next/ and .wrangler/ build artifacts. Vercel deploy path is unchanged; this is purely additive. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../open-brain-dashboard-next/.gitignore | 2 + .../open-brain-dashboard-next/README.md | 24 + .../open-next.config.ts | 3 + .../package-lock.json | 14524 +++++++++++----- .../open-brain-dashboard-next/package.json | 4 +- .../open-brain-dashboard-next/wrangler.jsonc | 10 + 6 files changed, 10131 insertions(+), 4436 deletions(-) create mode 100644 dashboards/open-brain-dashboard-next/open-next.config.ts create mode 100644 dashboards/open-brain-dashboard-next/wrangler.jsonc diff --git a/dashboards/open-brain-dashboard-next/.gitignore b/dashboards/open-brain-dashboard-next/.gitignore index 450ca1705..609c06893 100644 --- a/dashboards/open-brain-dashboard-next/.gitignore +++ b/dashboards/open-brain-dashboard-next/.gitignore @@ -1,6 +1,8 @@ node_modules/ .next/ .vercel/ +.open-next/ +.wrangler/ .env .env.local .env.local.example diff --git a/dashboards/open-brain-dashboard-next/README.md b/dashboards/open-brain-dashboard-next/README.md index 87e19fb53..86aba31a1 100644 --- a/dashboards/open-brain-dashboard-next/README.md +++ b/dashboards/open-brain-dashboard-next/README.md @@ -93,6 +93,30 @@ Or connect the folder to Vercel via the dashboard. Set the environment variables > [!TIP] > The free Vercel tier is sufficient. The dashboard makes server-side API calls to your Open Brain REST endpoint — there's no heavy compute. +### Step 5 (alternative): Deploy to Cloudflare Workers (optional) + +If you're already on Cloudflare for the [`open-brain-rest`](../../integrations/cloudflare-rest-worker/) gateway, you can host the dashboard on the same platform via the [`@opennextjs/cloudflare`](https://opennext.js.org/cloudflare) adapter. The older `@cloudflare/next-on-pages` adapter caps at Next 15.5.x and doesn't support this dashboard's Next 16. + +The repo ships the two config files this needs out of the box (`open-next.config.ts` and `wrangler.jsonc`); rename the Worker in `wrangler.jsonc` if you want something other than `ob-dashboard`. + +```bash +# 1. Make sure .env has NEXT_PUBLIC_API_URL set — it's read at *build* +# time and baked into the client bundle. +npx opennextjs-cloudflare build + +# 2. First-time deploy creates the Worker. +npx opennextjs-cloudflare deploy + +# 3. Set SESSION_SECRET as a *runtime* secret on the deployed Worker. +# (NEXT_PUBLIC_API_URL is build-time only, so no Worker secret for it.) +wrangler secret put SESSION_SECRET --name ob-dashboard +``` + +The dashboard ends up at `https://ob-dashboard..workers.dev`. + +> [!TIP] +> `NEXT_PUBLIC_API_URL` is build-time, `SESSION_SECRET` is runtime. If you change the API URL later you have to rebuild and redeploy; rotating the session secret only needs `wrangler secret put`. + ## Expected Outcome When working correctly: diff --git a/dashboards/open-brain-dashboard-next/open-next.config.ts b/dashboards/open-brain-dashboard-next/open-next.config.ts new file mode 100644 index 000000000..1a57e31cc --- /dev/null +++ b/dashboards/open-brain-dashboard-next/open-next.config.ts @@ -0,0 +1,3 @@ +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; + +export default defineCloudflareConfig({}); diff --git a/dashboards/open-brain-dashboard-next/package-lock.json b/dashboards/open-brain-dashboard-next/package-lock.json index 995a4b0cf..57355a43f 100644 --- a/dashboards/open-brain-dashboard-next/package-lock.json +++ b/dashboards/open-brain-dashboard-next/package-lock.json @@ -18,6 +18,7 @@ "server-only": "^0.0.1" }, "devDependencies": { + "@opennextjs/cloudflare": "^1.19.4", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -25,7 +26,8 @@ "eslint": "^9", "eslint-config-next": "16.2.4", "tailwindcss": "^4", - "typescript": "^5" + "typescript": "^5", + "wrangler": "^4.85.0" } }, "node_modules/@alloc/quick-lru": { @@ -41,2279 +43,7670 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "node_modules/@ast-grep/napi": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.40.5.tgz", + "integrity": "sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "engines": { + "node": ">= 10" }, + "optionalDependencies": { + "@ast-grep/napi-darwin-arm64": "0.40.5", + "@ast-grep/napi-darwin-x64": "0.40.5", + "@ast-grep/napi-linux-arm64-gnu": "0.40.5", + "@ast-grep/napi-linux-arm64-musl": "0.40.5", + "@ast-grep/napi-linux-x64-gnu": "0.40.5", + "@ast-grep/napi-linux-x64-musl": "0.40.5", + "@ast-grep/napi-win32-arm64-msvc": "0.40.5", + "@ast-grep/napi-win32-ia32-msvc": "0.40.5", + "@ast-grep/napi-win32-x64-msvc": "0.40.5" + } + }, + "node_modules/@ast-grep/napi-darwin-arm64": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-arm64/-/napi-darwin-arm64-0.40.5.tgz", + "integrity": "sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "node_modules/@ast-grep/napi-darwin-x64": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-x64/-/napi-darwin-x64-0.40.5.tgz", + "integrity": "sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "node_modules/@ast-grep/napi-linux-arm64-gnu": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-gnu/-/napi-linux-arm64-gnu-0.40.5.tgz", + "integrity": "sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">= 10" } }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "node_modules/@ast-grep/napi-linux-arm64-musl": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-musl/-/napi-linux-arm64-musl-0.40.5.tgz", + "integrity": "sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "node_modules/@ast-grep/napi-linux-x64-gnu": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-gnu/-/napi-linux-x64-gnu-0.40.5.tgz", + "integrity": "sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@ast-grep/napi-linux-x64-musl": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-musl/-/napi-linux-x64-musl-0.40.5.tgz", + "integrity": "sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "node_modules/@ast-grep/napi-win32-arm64-msvc": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-arm64-msvc/-/napi-win32-arm64-msvc-0.40.5.tgz", + "integrity": "sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "node_modules/@ast-grep/napi-win32-ia32-msvc": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-ia32-msvc/-/napi-win32-ia32-msvc-0.40.5.tgz", + "integrity": "sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@ast-grep/napi-win32-x64-msvc": { + "version": "0.40.5", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.40.5.tgz", + "integrity": "sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 10" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@dnd-kit/accessibility": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", - "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.6.2" }, - "peerDependencies": { - "react": ">=16.8.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@dnd-kit/core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", - "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@dnd-kit/accessibility": "^3.1.1", - "@dnd-kit/utilities": "^3.2.2", - "tslib": "^2.0.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@dnd-kit/sortable": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", - "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@dnd-kit/utilities": "^3.2.2", - "tslib": "^2.0.0" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@dnd-kit/core": "^6.3.0", - "react": ">=16.8.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@dnd-kit/utilities": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", - "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.0.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "react": ">=16.8.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "dev": true, - "license": "MIT", - "optional": true, + "license": "Apache-2.0", "dependencies": { - "@emnapi/wasi-threads": "1.2.0", - "tslib": "^2.4.0" + "tslib": "^2.6.2" } }, - "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", - "license": "MIT", - "optional": true, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.4.0" + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "dev": true, - "license": "MIT", - "optional": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=14.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@aws-sdk/client-cloudfront": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.984.0.tgz", + "integrity": "sha512-couDuDLpJtoeWne/nYyJ+I+5ntBVdNgBVRTCoDaXuVV7OC3u/wz5Ps0+GogspEwMLEFoOJ8t691h3YXQtnpQTw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.984.0.tgz", + "integrity": "sha512-8/Oft9MWQtbG6p9f8eY5fsKC2CcO5YVDlwive8eUYS9mEbgnyQxm68OyH26WvsSTykQ9QkIbR+fOG56RsIBODw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/dynamodb-codec": "^3.972.7", + "@aws-sdk/middleware-endpoint-discovery": "^3.972.3", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.984.0.tgz", + "integrity": "sha512-kqwNBIGNxGVhINwgN/UQfdsQkaMjbu9PFV2EhATWouV+RT60uMjK9JENgLDwbgJmEVbbnPsh9HaZ5KKwPSdiDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.984.0.tgz", + "integrity": "sha512-7ny2Slr93Y+QniuluvcfWwyDi32zWQfznynL56Tk0vVh7bWrvS/odm8WP2nInKicRVNipcJHY2YInur6Q/9V0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", + "@aws-sdk/middleware-expect-continue": "^3.972.3", + "@aws-sdk/middleware-flexible-checksums": "^3.972.4", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-location-constraint": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-s3": "^3.972.6", + "@aws-sdk/middleware-ssec": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/signature-v4-multi-region": "3.984.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-blob-browser": "^4.2.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/hash-stream-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/md5-js": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.10", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.984.0.tgz", + "integrity": "sha512-TDvHpOUWlpanc3xQ5Xw0y8L2hoojBFCCSmXQ/6rKqGOf1ScX3dMA+K9aF0Zp0iwjhSh4VvsHD42esl8XwQZDjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.6", + "@aws-sdk/credential-provider-node": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-sqs": "^3.972.5", + "@aws-sdk/middleware-user-agent": "^3.972.6", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.984.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.4", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.0", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/md5-js": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.12", + "@smithy/middleware-retry": "^4.4.29", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.28", + "@smithy/util-defaults-mode-node": "^4.2.31", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.974.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.5.tgz", + "integrity": "sha512-lMPlYlYfQdNZhlkJgnkmESwrY+hNh3PljmZ+37oAqLNdJ6rnILAwFSyc6B3bJeDOtMORNnMQIej0aTRuOlDyhQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/xml-builder": "^3.972.19", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.7.tgz", + "integrity": "sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.31.tgz", + "integrity": "sha512-X/yGB73LmDW/6MdDJGCDzZBUXnM3ys4vs9l+5ZTJmiEswDdP1OjeoAFlFjVGS9o4KB2wZWQ9KOfdVNSSK6Ep3w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.33.tgz", + "integrity": "sha512-c0ZF+lwoWVvX5iCaGKL5T/4DnIw88CGqxA0BcBs3U86mIp5EZYPVg+KSPkMXOyokmADvNewiMUfSG2uFwjRp0g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.35.tgz", + "integrity": "sha512-jsU4u/cRkKFLKQS0k918FQ27fzXLG5ENiLWQMYE6581zLeI2hWh04ptlrvZMB3wJT/5d+vSzJk74X1CMFr4y8Q==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/credential-provider-env": "^3.972.31", + "@aws-sdk/credential-provider-http": "^3.972.33", + "@aws-sdk/credential-provider-login": "^3.972.35", + "@aws-sdk/credential-provider-process": "^3.972.31", + "@aws-sdk/credential-provider-sso": "^3.972.35", + "@aws-sdk/credential-provider-web-identity": "^3.972.35", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=20.0.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.35.tgz", + "integrity": "sha512-5oa3j0cA50jPqgNhZ9XdJVopuzUf1klRb28/2MfLYWWiPi9DRVvbrBWT+DidbHTT36520VuXZJahQwR+YgSjrg==", "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.36", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.36.tgz", + "integrity": "sha512-4nT2T8Z7vH8KE9EdjEsuIlHpZSlcaK2PrKbQBjuUGU46BCCzF3WvP0u0Uiosni3Ykmmn4rWLVawoOCLotUtCbg==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.31", + "@aws-sdk/credential-provider-http": "^3.972.33", + "@aws-sdk/credential-provider-ini": "^3.972.35", + "@aws-sdk/credential-provider-process": "^3.972.31", + "@aws-sdk/credential-provider-sso": "^3.972.35", + "@aws-sdk/credential-provider-web-identity": "^3.972.35", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.31.tgz", + "integrity": "sha512-eKeT4MXumpBJsrDLCYcSzIkFPVTFn/es7It2oogp2OhU/ic7P/+xzFpQx9ZhwtXS57Mc5S42BPWi7lHmvs/nYg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.35.tgz", + "integrity": "sha512-bCuBdfnj0KGDMdLp6utMTLiJcFN2ek9EgZinxQZZSc3FxjJ/HSqeqab2cjbnoNfy8RM6suDCsRkmVY1izp9I+A==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/token-providers": "3.1036.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18.0" + "node": ">=20.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.35.tgz", + "integrity": "sha512-swW6Bwvl8lanyEMtZOWE/oR6yqcRQH4HTQZUVsnDVgoXvRjRywpYpLv2BWwjUFyjPrqsdX6FeTkf4tMSe/qFTQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.18.0" + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@aws-sdk/dynamodb-codec": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.5.tgz", + "integrity": "sha512-SNF2VCwK2MA9P3jmz/DW498BK0SkGMyLBeAe+ZJy2fVoEOHq0b/ZdRNw4fQMuq07gdNxLErY9RnXUo6Rdu2QIA==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@smithy/core": "^3.23.17", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.5.tgz", + "integrity": "sha512-itVdge0NozgtgmtbZ25FVwWU3vGlE7x7feE/aOEJNkQfEpbkrF8Rj1QmnK+2blFfYE1xWt/iU+6/jUp/pv1+MA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.10.tgz", + "integrity": "sha512-Vbc2frZH7wXlMNd+ZZSXUEs/l1Sv8Jj4zUnIfwrYF5lwaLdXHZ9xx4U3rjUcaye3HRhFVc+E5DbBxpRAbB16BA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.11.tgz", + "integrity": "sha512-vXARCZVFQHdsd6qPPZyC/hh+5x2XsCYKqUQDCqnUlpGpChMpDojOOacQWdLJ+FFXKN8X3cmLOGrtgx/zysCKqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/endpoint-cache": "^3.972.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.10.tgz", + "integrity": "sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.974.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.13.tgz", + "integrity": "sha512-b6QUe2hQX9XsnCzp6mtzVaERhganDKeb8lmGL6pVhr7rRVH9S9keDFW7uKytuuqmcY5943FixoGqn/QL+sbUBA==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/crc64-nvme": "^3.972.7", + "@aws-sdk/types": "^3.973.8", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.10.tgz", + "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.10.tgz", + "integrity": "sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.10.tgz", + "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.11.tgz", + "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.34", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.34.tgz", + "integrity": "sha512-/UL96JKjsjdodcRRMKl99tLQvK6Oi9ptLC9iU1yiTF/ruaDX0mtBBtnLNZDxIZRJOCVOtB49ed1YaTadqygk8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.972.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.22.tgz", + "integrity": "sha512-DtR3mEiOUJcnEX/QuXmvbJto6xvQzp2ftnHb29c0aQYdmmzbKf0gsu9ovx1i/yy4ZR6m0rttTucS0iiP32dlGA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.10.tgz", + "integrity": "sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.35.tgz", + "integrity": "sha512-hOFWNOjVmOocpRlrU04nYxjMOeoe0Obu5AXEuhB8zblMCPl3cG1hdluQCZERRKFyhMQjwZnDbhSHjoMUjetFGw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-retry": "^4.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz", + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-endpoints": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.3.tgz", + "integrity": "sha512-SivE6GP228IVgfsrr2c/vqTg95X0Qj39Yw4uIrcddpkUzIltNMoNOR62leHOLhODfjv9K8X2mPTwS69A5kT0nQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/signature-v4-multi-region": "^3.996.22", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.21", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.5", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.4", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.22.tgz", + "integrity": "sha512-/rXhMXteD+BqhFd0nYprAgcZ/KtU+963uftPqd3tiFcFfooHZINXUGtOmo2SQjRVauCTNqIEzkwuSETdZFqTTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.34", + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz", + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-endpoints": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz", + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/config-resolver": "^4.4.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.984.0.tgz", + "integrity": "sha512-TaWbfYCwnuOSvDSrgs7QgoaoXse49E7LzUkVOUhoezwB7bkmhp+iojADm7UepCEu4021SquD7NG1xA+WCvmldA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.6", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1036.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1036.0.tgz", + "integrity": "sha512-aNSJ6jjDYayxN9ZA1JpycVScX93Lx03kKZ1EXt3DGOTahcWVLJj3oLAlop0xKP+vP2Ga2t49p1tEaMkTbCCaZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.5", + "@aws-sdk/nested-clients": "^3.997.3", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.984.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.984.0.tgz", + "integrity": "sha512-9ebjLA0hMKHeVvXEtTDCCOBtwjb0bOXiuUV06HNeVdgAjH6gj4x4Zwt4IBti83TiyTGOCl5YfZqGx4ehVsasbQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.10.tgz", + "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.21.tgz", + "integrity": "sha512-Av4UHTcAWgdvbN0IP9pbtf4Qa1+6LtJqQdZWj5pLn5J67w0pnJJAZZ+7JPPcj2KN3378zD2JDM9DwJKEyvyMTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.35", + "@aws-sdk/types": "^3.973.8", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.19.tgz", + "integrity": "sha512-Cw8IOMdBUEIl8ZlhRC3Dc/E64D5B5/8JhV6vhPLiPfJwcRC84S6F8aBOIi/N4vR9ZyA4I5Cc0Ateb/9EHaJXeQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "fast-xml-parser": "5.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", + "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@cloudflare/unenv-preset": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz", + "integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "peerDependencies": { + "unenv": "2.0.0-rc.24", + "workerd": ">1.20260305.0 <2.0.0-0" + }, + "peerDependenciesMeta": { + "workerd": { + "optional": true + } + } + }, + "node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260424.1.tgz", + "integrity": "sha512-yFR1XaJbSDLg/qbwtrYaU2xwFXatIPKR5nrMQCN1q/m6+Qe/j6r+kCnFEvOJjMZOm9iCKsE6Qly5clgl4u32qw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260424.1.tgz", + "integrity": "sha512-LqWKcE7x/9KyC2iQvKPeb20hKST3dYXDZlYTvFymgR1DfLS0OFOCzVGTloVNd7WqvK4SkdzBYfxo7QMIAeBK0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260424.1.tgz", + "integrity": "sha512-YlEBFbAYZHe/ylzl8WEYQEU/jr+0XMqXaST2oBk5oVjksdb1NGuJaggluCdZAzuJJ8UqdTmyhY5u/qrasbiFWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260424.1.tgz", + "integrity": "sha512-qJ0X0m6cL8fWDUPDg8K4IxYZXNJI6XbeOihqjnqKbAClrjdPDn8VUSd+z2XiCQ5NylMtMrpa/skC9UfaR6mh8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260424.1.tgz", + "integrity": "sha512-tZ7Z9qmYNAP6z1/+8r/zKbk8F8DZmpmwNzMeN+zkde2Wnhfr3FBqOkJXT/5zmli8HPoWrIXxSiyqcNDMy8V2Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dotenvx/dotenvx": { + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.31.0.tgz", + "integrity": "sha512-GeDxvtjiRuoyWVU9nQneId879zIyNdL05bS7RKiqMkfBSKpHMWHLoRyRqjYWLaXmX/llKO1hTlqHDmatkQAjPA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "commander": "^11.1.0", + "dotenv": "^16.4.5", + "eciesjs": "^0.4.10", + "execa": "^5.1.1", + "fdir": "^6.2.0", + "ignore": "^5.3.0", + "object-treeify": "1.1.33", + "picomatch": "^4.0.2", + "which": "^4.0.0" + }, + "bin": { + "dotenvx": "src/cli/dotenvx.js", + "git-dotenvx": "src/cli/dotenvx.js" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@ecies/ciphers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.6.tgz", + "integrity": "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==", + "dev": true, + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2.7.10", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-linux-arm": { "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.4.tgz", + "integrity": "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.4.tgz", + "integrity": "sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz", + "integrity": "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz", + "integrity": "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz", + "integrity": "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz", + "integrity": "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz", + "integrity": "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz", + "integrity": "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz", + "integrity": "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz", + "integrity": "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@node-minify/core": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@node-minify/core/-/core-8.0.6.tgz", + "integrity": "sha512-/vxN46ieWDLU67CmgbArEvOb41zlYFOkOtr9QW9CnTrBLuTyGgkyNWC2y5+khvRw3Br58p2B5ZVSx/PxCTru6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@node-minify/utils": "8.0.6", + "glob": "9.3.5", + "mkdirp": "1.0.4" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@node-minify/core/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@node-minify/core/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@node-minify/core/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@node-minify/core/node_modules/minimatch": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@node-minify/core/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@node-minify/core/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@node-minify/core/node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@node-minify/terser": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@node-minify/terser/-/terser-8.0.6.tgz", + "integrity": "sha512-grQ1ipham743ch2c3++C8Isk6toJnxJSyDiwUI/IWUCh4CZFD6aYVw6UAY40IpCnjrq5aXGwiv5OZJn6Pr0hvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@node-minify/utils": "8.0.6", + "terser": "5.16.9" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@node-minify/utils": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@node-minify/utils/-/utils-8.0.6.tgz", + "integrity": "sha512-csY4qcR7jUwiZmkreNTJhcypQfts2aY2CK+a+rXgXUImZiZiySh0FvwHjRnlqWKvg+y6ae9lHFzDRjBTmqlTIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "gzip-size": "6.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@opennextjs/aws": { + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/@opennextjs/aws/-/aws-3.10.4.tgz", + "integrity": "sha512-xVmWHGdptJgVhQivuoeAYqsWpIgGoDEeZJC6AYMgvQYisDicGuS7gh10Z8MEmrgsJxNGdZ0arCCDieGxM88afw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ast-grep/napi": "^0.40.5", + "@aws-sdk/client-cloudfront": "3.984.0", + "@aws-sdk/client-dynamodb": "3.984.0", + "@aws-sdk/client-lambda": "3.984.0", + "@aws-sdk/client-s3": "3.984.0", + "@aws-sdk/client-sqs": "3.984.0", + "@node-minify/core": "^8.0.6", + "@node-minify/terser": "^8.0.6", + "@tsconfig/node18": "^1.0.3", + "aws4fetch": "^1.0.20", + "chalk": "^5.6.2", + "cookie": "^1.0.2", + "esbuild": "0.25.4", + "express": "^5.1.0", + "path-to-regexp": "^6.3.0", + "urlpattern-polyfill": "^10.1.0", + "yaml": "^2.8.1" + }, + "bin": { + "open-next": "dist/index.js" + }, + "peerDependencies": { + "next": ">=15.5.15 <16 || >=16.2.3" + } + }, + "node_modules/@opennextjs/aws/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@opennextjs/aws/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@opennextjs/cloudflare": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@opennextjs/cloudflare/-/cloudflare-1.19.4.tgz", + "integrity": "sha512-sRJmLFz6DqGzoToSTPyyQhxozXGgF/YsnHpgiCe2XTugfZZnfetMoI4BolFP65XAzIV3tdSSzCb+etK5s8UEUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ast-grep/napi": "^0.40.5", + "@dotenvx/dotenvx": "1.31.0", + "@opennextjs/aws": "3.10.4", + "ci-info": "^4.2.0", + "cloudflare": "^4.4.1", + "comment-json": "^4.5.1", + "enquirer": "^2.4.1", + "glob": "^12.0.0", + "ts-tqdm": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "opennextjs-cloudflare": "dist/cli/index.js" + }, + "peerDependencies": { + "next": ">=15.5.15 <16 || >=16.2.3", + "wrangler": "^4.84.1" + } + }, + "node_modules/@poppinss/colors": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/dumper/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.17", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz", + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.17", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.17.tgz", + "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.14.tgz", + "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.14.tgz", + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.14.tgz", + "integrity": "sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.14.tgz", + "integrity": "sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.14.tgz", + "integrity": "sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.14.tgz", + "integrity": "sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.17", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.17.tgz", + "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.15.tgz", + "integrity": "sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.14.tgz", + "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.14.tgz", + "integrity": "sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.14.tgz", + "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.14.tgz", + "integrity": "sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.14.tgz", + "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.32", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.32.tgz", + "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-middleware": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.6.tgz", + "integrity": "sha512-5zhmo2AkstmM/RMKYP0NHfmuYWBR+/umlmSuALgajLxf0X0rLE6d17MfzTxpzkILWVhwvCJkCyPH0AfMlbaucQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/service-error-classification": "^4.3.1", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.5", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.20.tgz", + "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.14.tgz", + "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.14.tgz", + "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.6.1.tgz", + "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.14.tgz", + "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.14.tgz", + "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.14.tgz", + "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.14.tgz", + "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.1.tgz", + "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.9.tgz", + "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.14.tgz", + "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.13", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.13.tgz", + "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.17", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz", + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.14.tgz", + "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.49", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.49.tgz", + "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.54", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.54.tgz", + "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.17", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz", + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.14.tgz", + "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.5.tgz", + "integrity": "sha512-h1IJsbgMDA+jaTjrco/JsyfWOgHRJBv8myB1y4AEI2fjIzD6ktZ7pFAyTw+gwN9GKIAygvC6db0mq0j8N2rFOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.3.1", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.25", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.25.tgz", + "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.3.0.tgz", + "integrity": "sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz", + "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tsconfig/node18": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-1.0.3.tgz", + "integrity": "sha512-RbwvSJQsuN9TB04AQbGULYfOGE/RnSFk/FLQ5b0NmDf5Kx2q/lABZbHQPKCO1vZ6Fiwkplu+yb9pGdLy1iGseQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", + "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/type-utils": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.57.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", + "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", + "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.1", + "@typescript-eslint/types": "^8.57.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", + "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", + "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", + "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", + "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", + "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.1", + "@typescript-eslint/tsconfig-utils": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", + "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", + "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", "cpu": [ - "arm" + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/axe-core": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.9.tgz", + "integrity": "sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, - "funding": { - "url": "https://opencollective.com/libvips" + "bin": { + "browserslist": "cli.js" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "node": ">= 0.8" } }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "engines": { + "node": ">= 0.4" } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "node": ">=6" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" + "node_modules/caniuse-lite": { + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } ], + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "node": ">=8" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", "dependencies": { - "@emnapi/runtime": "^1.7.0" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=20" } }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], + "node_modules/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], + "node_modules/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/libvips" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/cloudflare": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/cloudflare/-/cloudflare-4.5.0.tgz", + "integrity": "sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/cloudflare/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/cloudflare/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" }, - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/comment-json": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", + "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "engines": { + "node": ">=6.6.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@next/env": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.4.tgz", - "integrity": "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==", + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, - "node_modules/@next/eslint-plugin-next": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.4.tgz", - "integrity": "sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA==", + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "3.3.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz", - "integrity": "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==", - "cpu": [ - "arm64" - ], + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">= 10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz", - "integrity": "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==", - "cpu": [ - "x64" - ], + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, "engines": { - "node": ">= 10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz", - "integrity": "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">= 10" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz", - "integrity": "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, "engines": { - "node": ">= 10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz", - "integrity": "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": ">= 10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz", - "integrity": "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">=0.4.0" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz", - "integrity": "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==", - "cpu": [ - "arm64" - ], + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 10" + "node": ">= 0.8" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz", - "integrity": "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eciesjs": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", + "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==", "dev": true, "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.5", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0" + }, "engines": { - "node": ">=12.4.0" + "bun": ">=1", + "deno": ">=2", + "node": ">=16" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true, "license": "MIT" }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" + "node_modules/electron-to-chromium": { + "version": "1.5.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", + "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-x64": "4.2.2", - "@tailwindcss/oxide-freebsd-x64": "4.2.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-x64-musl": "4.2.2", - "@tailwindcss/oxide-wasm32-wasi": "4.2.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + "engines": { + "node": ">=8.6" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", - "cpu": [ - "arm64" - ], + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", - "cpu": [ - "arm64" - ], + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", - "cpu": [ - "x64" - ], + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", - "cpu": [ - "x64" - ], + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", - "cpu": [ - "arm" - ], + "node_modules/es-iterator-helpers": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", + "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0", + "safe-array-concat": "^1.1.3" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", - "cpu": [ - "arm64" - ], + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", - "cpu": [ - "arm64" - ], + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", - "cpu": [ - "x64" - ], + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "hasown": "^2.0.2" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" } }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", - "cpu": [ - "x64" - ], + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, "engines": { - "node": ">= 20" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=14.0.0" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", - "cpu": [ - "arm64" - ], + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 20" + "node": ">=6" } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", - "cpu": [ - "x64" - ], + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 20" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tailwindcss/postcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", - "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", - "postcss": "^8.5.6", - "tailwindcss": "4.2.2" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "node_modules/eslint-config-next": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.4.tgz", + "integrity": "sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@next/eslint-plugin-next": "16.2.4", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "csstype": "^3.2.2" + "ms": "^2.1.1" } }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, "peerDependencies": { - "@types/react": "^19.2.0" + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", - "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/type-utils": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "debug": "^3.2.7" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=4" }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.57.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", - "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "debug": "^4.4.3" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=4" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "ms": "^2.1.1" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=4.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=4" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", - "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": "18 || 20 || >=22" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "BSD-2-Clause", "dependencies": { - "brace-expansion": "^5.0.2" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "bin": { - "semver": "bin/semver.js" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", - "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1" + "estraverse": "^5.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=0.10" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "eslint-visitor-keys": "^5.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=4.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "Apache-2.0", + "license": "BSD-2-Clause", "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4.0" } }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": ">=6" + } }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], + "node_modules/fast-xml-builder": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", + "integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "path-expression-matcher": "^1.1.3" + } }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], + "node_modules/fast-xml-parser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz", + "integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=16" } }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "ISC" }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.4.0" + "node": ">= 6" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "mime-db": "1.52.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">= 0.6" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 12.20" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, - "license": "Python-2.0" + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.8" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", - "call-bound": "^1.0.4", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -2322,151 +7715,121 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -2475,145 +7838,117 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axe-core": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", - "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/glob": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-12.0.0.tgz", + "integrity": "sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==", "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.9.tgz", - "integrity": "sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==", - "license": "Apache-2.0", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, "bin": { - "baseline-browser-mapping": "dist/cli.cjs" + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=6.0.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" } }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "18 || 20 || >=22" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">= 0.4" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bound": { + "node_modules/globalthis": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2622,141 +7957,86 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001780", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", - "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "duplexer": "^0.1.2" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" + "dunder-proto": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2765,34 +8045,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/inspect-js" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -2801,303 +8074,250 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", "dev": true, "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "hermes-estree": "0.25.1" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=10.17.0" } }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" + "ms": "^2.0.0" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.321", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", - "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, "engines": { - "node": ">=10.13.0" + "node": ">= 4" } }, - "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.19" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { "node": ">= 0.4" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.10" } }, - "node_modules/es-iterator-helpers": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", - "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", - "dev": true, + "node_modules/iron-session": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/iron-session/-/iron-session-8.0.4.tgz", + "integrity": "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA==", + "funding": [ + "https://github.com/sponsors/vvo", + "https://github.com/sponsors/brc-dd" + ], "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" + "cookie": "^0.7.2", + "iron-webcrypto": "^1.2.1", + "uncrypto": "^0.1.3" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-shim-unscopables": { + "node_modules/is-bigint": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "has-bigints": "^1.0.2" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3106,357 +8326,300 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "hasown": "^2.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-config-next": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.4.tgz", - "integrity": "sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.4", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^7.0.0", - "globals": "16.4.0", - "typescript-eslint": "^8.46.0" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, - "peerDependencies": { - "eslint": ">=9.0.0", - "typescript": ">=3.3.1" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-config-next/node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^3.2.7" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=4" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3465,737 +8628,929 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "which-typed-array": "^1.1.16" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dev": true, - "license": "BSD-2-Clause", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, "engines": { - "node": ">=4.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=8.6.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } + "license": "MIT" }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=16.0.0" + "node": ">=6" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "json-buffer": "3.0.1" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true, "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, "engines": { - "node": ">=16" + "node": ">=6" } }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true, - "license": "ISC" + "license": "CC0-1.0" }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "language-subtag-registry": "^0.3.20" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.13.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/has-symbols": { + "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, "license": "MIT" }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "hermes-estree": "0.25.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">= 0.6" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">=6" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "node_modules/miniflare": { + "version": "4.20260424.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260424.0.tgz", + "integrity": "sha512-B6MKBBd5TJ19daUc3Ae9rWctn1nDA/VCXykXfCsp9fTxyfGxnZY27tJs1caxgE9MWEMMKGbGHouqVtgKbKGxmw==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "@cspotcode/source-map-support": "0.8.1", + "sharp": "^0.34.5", + "undici": "7.24.8", + "workerd": "1.20260424.1", + "ws": "8.18.0", + "youch": "4.1.0-beta.10" + }, + "bin": { + "miniflare": "bootstrap.js" }, "engines": { - "node": ">= 0.4" + "node": ">=18.0.0" } }, - "node_modules/iron-session": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/iron-session/-/iron-session-8.0.4.tgz", - "integrity": "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA==", - "funding": [ - "https://github.com/sponsors/vvo", - "https://github.com/sponsors/brc-dd" - ], - "license": "MIT", + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", "dependencies": { - "cookie": "^0.7.2", - "iron-webcrypto": "^1.2.1", - "uncrypto": "^0.1.3" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/brc-dd" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", "dev": true, "license": "MIT", "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "obliterator": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" + "bin": { + "napi-postinstall": "lib/cli.js" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/napi-postinstall" } }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.4.tgz", + "integrity": "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==", + "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "@next/env": "16.2.4", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" }, "engines": { - "node": ">= 0.4" + "node": ">=20.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.4", + "@next/swc-darwin-x64": "16.2.4", + "@next/swc-linux-arm64-gnu": "16.2.4", + "@next/swc-linux-arm64-musl": "16.2.4", + "@next/swc-linux-x64-gnu": "16.2.4", + "@next/swc-linux-x64-musl": "16.2.4", + "@next/swc-win32-arm64-msvc": "16.2.4", + "@next/swc-win32-x64-msvc": "16.2.4", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } } }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-bun-module/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": ">=10" + "node": "^10 || ^12 || >=14" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.5.0" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" }, "engines": { "node": ">= 0.4" @@ -4204,60 +9559,63 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": "4.x || >=6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "path-key": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, "engines": { "node": ">= 0.4" }, @@ -4265,58 +9623,40 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", "dev": true, "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 10" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4324,25 +9664,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" } }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4351,31 +9699,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4383,79 +9733,81 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "ee-first": "1.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "wrappy": "1" } }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" + "dependencies": { + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.16" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4463,783 +9815,775 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0" }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" + "callsites": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" + "engines": { + "node": ">= 0.8" } }, - "node_modules/js-tokens": { + "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, - "license": "MIT" + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, "engines": { - "node": ">=4.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "language-subtag-registry": "^0.3.20" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=0.10" + "node": "^10 || ^12 || >=14" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, - "license": "MPL-2.0", + "license": "MIT", "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 0.10" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=6" } }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=0.6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "license": "MIT" }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 0.6" } }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">= 0.10" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=0.10.0" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "react": "^19.2.4" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, "engines": { - "node": ">= 12.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, "engines": { - "node": ">= 12.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", - "engines": { - "node": ">= 8" + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=8.6" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", "bin": { - "nanoid": "bin/nanoid.cjs" + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">= 18" }, "funding": { - "url": "https://opencollective.com/napi-postinstall" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", "license": "MIT" }, - "node_modules/next": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.4.tgz", - "integrity": "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { - "@next/env": "16.2.4", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.9.19", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.4", - "@next/swc-darwin-x64": "16.2.4", - "@next/swc-linux-arm64-gnu": "16.2.4", - "@next/swc-linux-arm64-musl": "16.2.4", - "@next/swc-linux-x64-gnu": "16.2.4", - "@next/swc-linux-x64-musl": "16.2.4", - "@next/swc-win32-arm64-msvc": "16.2.4", - "@next/swc-win32-x64-msvc": "16.2.4", - "sharp": "^0.34.5" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.4" } }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { - "array.prototype.flatmap": "^1.3.3", + "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "shebang-regex": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5248,33 +10592,34 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -5283,303 +10628,362 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.values": { + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/string-width/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=8" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "engines": { + "node": ">=6" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/strnum": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT" }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "has-flag": "^4.0.0" }, - "peerDependencies": { - "react": "^19.2.4" + "engines": { + "node": ">=8" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, "engines": { "node": ">= 0.4" }, @@ -5587,322 +10991,338 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "node_modules/terser": { + "version": "5.16.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz", + "integrity": "sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, "bin": { - "resolve": "bin/resolve" + "terser": "bin/terser" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, "engines": { - "node": ">=0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.0" } }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "node_modules/ts-tqdm": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/ts-tqdm/-/ts-tqdm-0.8.6.tgz", + "integrity": "sha512-3X3M1PZcHtgQbnwizL+xU8CAgbYbeLHrrDwL9xxcZZrV5J+e7loJm1XrXozHjSkl44J0Zg0SgA8rXbh83kCkcQ==", + "dev": true, "license": "MIT" }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, "bin": { - "semver": "bin/semver.js" + "json5": "lib/cli.js" } }, - "node_modules/server-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", - "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", - "license": "MIT" + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8.0" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sharp/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=8" + "node": ">=14.17" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/typescript-eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", + "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.1", + "@typescript-eslint/parser": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/side-channel": { + "node_modules/unbox-primitive": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -5911,127 +11331,198 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.8.tgz", + "integrity": "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=20.18.1" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, + "pathe": "^2.0.3" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "update-browserslist-db": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", "dev": true, "license": "MIT" }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8" } }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">= 0.4" + "node": ">= 8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -6040,31 +11531,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -6073,17 +11559,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -6092,16 +11578,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6110,575 +11600,739 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/workerd": { + "version": "1.20260424.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260424.1.tgz", + "integrity": "sha512-oKsB0Xo/mfkYMdSACoS06XZg09VUK4rXwHfF/1t3P++sMbwzf4UHQvMO57+zxpEB2nVrY/ZkW0bYFGq4GdAFSQ==", "dev": true, - "license": "MIT", + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "workerd": "bin/workerd" + }, "engines": { - "node": ">=8" + "node": ">=16" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20260424.1", + "@cloudflare/workerd-darwin-arm64": "1.20260424.1", + "@cloudflare/workerd-linux-64": "1.20260424.1", + "@cloudflare/workerd-linux-arm64": "1.20260424.1", + "@cloudflare/workerd-windows-64": "1.20260424.1" } }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", + "node_modules/wrangler": { + "version": "4.85.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.85.0.tgz", + "integrity": "sha512-93cwt2RPb1qdcmEgPzH7ybiLN4BIKoWpscIX6SywjHrQOeIZrQk2haoc3XMLKtQTmzapxza9OuDD+kMHpsuuhg==", + "dev": true, + "license": "MIT OR Apache-2.0", "dependencies": { - "client-only": "0.0.1" + "@cloudflare/kv-asset-handler": "0.4.2", + "@cloudflare/unenv-preset": "2.16.1", + "blake3-wasm": "2.1.5", + "esbuild": "0.27.3", + "miniflare": "4.20260424.0", + "path-to-regexp": "6.3.0", + "unenv": "2.0.0-rc.24", + "workerd": "1.20260424.1" + }, + "bin": { + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" }, "engines": { - "node": ">= 12.0.0" + "node": ">=20.3.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + "@cloudflare/workers-types": "^4.20260424.1" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { + "@cloudflare/workers-types": { "optional": true } } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/wrangler/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=18" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=18" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/wrangler/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=18" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8.0" + "node": ">=18" } }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">=18" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=18" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "node_modules/wrangler/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "node_modules/wrangler/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", "engines": { - "node": ">=14.17" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/typescript-eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", - "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.1", - "@typescript-eslint/parser": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" + "engines": { + "node": ">=10.0.0" }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "utf-8-validate": { + "optional": true } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "license": "ISC", + "engines": { + "node": ">=10" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, "bin": { - "node-which": "bin/node-which" + "yaml": "bin.mjs" }, "engines": { - "node": ">= 8" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "node_modules/youch": { + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", "dev": true, "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/youch/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/zod": { diff --git a/dashboards/open-brain-dashboard-next/package.json b/dashboards/open-brain-dashboard-next/package.json index 1f3d4ea82..b9723dbfa 100644 --- a/dashboards/open-brain-dashboard-next/package.json +++ b/dashboards/open-brain-dashboard-next/package.json @@ -19,6 +19,7 @@ "server-only": "^0.0.1" }, "devDependencies": { + "@opennextjs/cloudflare": "^1.19.4", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -26,6 +27,7 @@ "eslint": "^9", "eslint-config-next": "16.2.4", "tailwindcss": "^4", - "typescript": "^5" + "typescript": "^5", + "wrangler": "^4.85.0" } } diff --git a/dashboards/open-brain-dashboard-next/wrangler.jsonc b/dashboards/open-brain-dashboard-next/wrangler.jsonc new file mode 100644 index 000000000..53a96b2df --- /dev/null +++ b/dashboards/open-brain-dashboard-next/wrangler.jsonc @@ -0,0 +1,10 @@ +{ + "name": "ob-dashboard", + "compatibility_date": "2026-04-25", + "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], + "main": ".open-next/worker.js", + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS" + } +} From 0609b47069050489134cc3cec2d0e6f9917a9e42 Mon Sep 17 00:00:00 2001 From: snapsynapse <57973674+snapsynapse@users.noreply.github.com> Date: Sat, 2 May 2026 22:51:35 -0600 Subject: [PATCH 061/125] [dashboards] Canonical landing page for openbrain.fyi Adds dashboards/ob1-canonical-landing/, a self-contained static landing page targeting https://openbrain.fyi via GitHub Pages. No build step, no dependencies. Includes: - index.html with full SEO meta, OG/Twitter cards, JSON-LD (TechArticle + DefinedTerm), skip-link, sticky-nav glass effect, hero gradient, prefers-reduced-motion handling - 404.html (branded, noindex) with brand-blue gradient - Brand assets generated via ImageMagick: favicon-32, apple-touch-icon (180), square logo (512), wide hero banner (1200), social OG (1200x630 composited on brand-navy) - Crawler companions: sitemap.xml, robots.txt, llms.txt, site.webmanifest - CNAME bound to openbrain.fyi - README.md with step-by-step deploy guide (workflow YAML, DNS table, Pages enable + custom domain + HTTPS, optional org-level domain verification, domain donation handover step) - MAINTENANCE.md covering edit-and-deploy loop, copy/video/logo updates, pre-deploy validation (JSON-LD, a11y, link health, size budget), post-deploy verification, decommissioning WCAG 2.1 AA, single h1, validated JSON-LD, gate-schema-compliant metadata.json. Page weight: 44KB HTML + 108KB images. Co-Authored-By: Claude Sonnet 4.6 --- dashboards/ob1-canonical-landing/404.html | 68 ++ dashboards/ob1-canonical-landing/CNAME | 1 + .../ob1-canonical-landing/MAINTENANCE.md | 222 +++++ dashboards/ob1-canonical-landing/README.md | 158 +++ .../imgs/apple-touch-icon.png | Bin 0 -> 9416 bytes .../ob1-canonical-landing/imgs/favicon-32.png | Bin 0 -> 1446 bytes .../imgs/ob1-logo-wide.png | Bin 0 -> 29005 bytes .../ob1-canonical-landing/imgs/ob1-logo.png | Bin 0 -> 28145 bytes dashboards/ob1-canonical-landing/imgs/og.png | Bin 0 -> 29487 bytes dashboards/ob1-canonical-landing/index.html | 931 ++++++++++++++++++ dashboards/ob1-canonical-landing/llms.txt | 36 + .../ob1-canonical-landing/metadata.json | 18 + dashboards/ob1-canonical-landing/robots.txt | 4 + .../ob1-canonical-landing/site.webmanifest | 32 + dashboards/ob1-canonical-landing/sitemap.xml | 9 + 15 files changed, 1479 insertions(+) create mode 100644 dashboards/ob1-canonical-landing/404.html create mode 100644 dashboards/ob1-canonical-landing/CNAME create mode 100644 dashboards/ob1-canonical-landing/MAINTENANCE.md create mode 100644 dashboards/ob1-canonical-landing/README.md create mode 100644 dashboards/ob1-canonical-landing/imgs/apple-touch-icon.png create mode 100644 dashboards/ob1-canonical-landing/imgs/favicon-32.png create mode 100644 dashboards/ob1-canonical-landing/imgs/ob1-logo-wide.png create mode 100644 dashboards/ob1-canonical-landing/imgs/ob1-logo.png create mode 100644 dashboards/ob1-canonical-landing/imgs/og.png create mode 100644 dashboards/ob1-canonical-landing/index.html create mode 100644 dashboards/ob1-canonical-landing/llms.txt create mode 100644 dashboards/ob1-canonical-landing/metadata.json create mode 100644 dashboards/ob1-canonical-landing/robots.txt create mode 100644 dashboards/ob1-canonical-landing/site.webmanifest create mode 100644 dashboards/ob1-canonical-landing/sitemap.xml diff --git a/dashboards/ob1-canonical-landing/404.html b/dashboards/ob1-canonical-landing/404.html new file mode 100644 index 000000000..3d605d34e --- /dev/null +++ b/dashboards/ob1-canonical-landing/404.html @@ -0,0 +1,68 @@ + + + + + + Page not found — Open Brain + + + + + + + + +
+ +
+
404
+

Page not found

+

This page doesn’t exist. The brain has no memory of it.

+ Return home → + + diff --git a/dashboards/ob1-canonical-landing/CNAME b/dashboards/ob1-canonical-landing/CNAME new file mode 100644 index 000000000..a72a42c87 --- /dev/null +++ b/dashboards/ob1-canonical-landing/CNAME @@ -0,0 +1 @@ +openbrain.fyi \ No newline at end of file diff --git a/dashboards/ob1-canonical-landing/MAINTENANCE.md b/dashboards/ob1-canonical-landing/MAINTENANCE.md new file mode 100644 index 000000000..231e98f6d --- /dev/null +++ b/dashboards/ob1-canonical-landing/MAINTENANCE.md @@ -0,0 +1,222 @@ +# Maintaining openbrain.fyi + +This guide is for the OB1 maintainer and any future contributors who need to update the canonical landing page after it goes live. The page is intentionally a single static `index.html` with no build step — every change is a normal git edit, and every deploy is a single push. + +## Edit-and-deploy loop + +1. Edit any file under `dashboards/ob1-canonical-landing/` +2. Open a PR (or push to `main` if you have direct write access) +3. The `Deploy landing page` workflow fires on the path filter and pushes the artifact to Pages +4. The `https://openbrain.fyi/` cache refreshes within 1–2 minutes of the deploy job finishing + +Watch progress at **Actions → Deploy landing page**. The job's environment URL links to the live site once it's published. + +## Common edits + +### Update copy, headlines, or links + +All visible text lives directly in `index.html`. The major sections are marked with HTML `id`s for navigation: + +| `id` | Section | +|------|---------| +| `main-content` | Skip-link target, wraps everything below the nav | +| (hero, no id) | The first `
` — h1, byline, CTA buttons, demo video | +| `how-it-works` | Three-pillar overview | +| `get-started` | Numbered steps + AI-assistant cards + companion prompts | +| `extensions` | Extensions table | +| `community` | Recipes + tools + skills tables, dashboards/integrations pillars | + +After any copy change, update `dateModified` in three places so search engines and citations stay in sync: + +| File | Field | +|------|-------| +| `index.html` | `` | +| `index.html` | JSON-LD `TechArticle` → `"dateModified"` | +| `index.html` | Byline `` | +| `sitemap.xml` | `` | +| `metadata.json` | `"updated"` | + +All five should be the same ISO date (`YYYY-MM-DD`). + +### Add or swap a video + +Videos use GitHub user-attachment URLs (the same URLs that render in the project README). To add a new video: + +1. Drag the `.mp4` into a GitHub issue or PR comment to upload it; copy the `https://github.com/user-attachments/assets/...` URL +2. Embed using the existing pattern: + +```html + +``` + +Always set a meaningful `aria-label` — screen readers announce it. Use `preload="metadata"` only for the hero video; keep all below-fold videos at `preload="none"` to protect first-paint performance. + +If a GitHub user-attachment URL ever goes 404, replace with a re-uploaded asset; do not host video binaries in the repo (gate rule blocks files >1MB). + +### Replace or update the logo + +The page ships three brand-image variants generated from a single source: + +| File | Use | Source | +|------|-----|--------| +| `imgs/ob1-logo.png` (512×512) | Nav + manifest icon | Square master | +| `imgs/ob1-logo-wide.png` (1200×360) | Hero banner | Wide master | +| `imgs/og.png` (1200×630) | Social share | Wide on `#0f1b33` background | +| `imgs/apple-touch-icon.png` (180×180) | iOS home screen | Square master | +| `imgs/favicon-32.png` (32×32) | Browser tab | Square master | + +To regenerate from new master images, drop the new sources at `imgs/_master-square.png` and `imgs/_master-wide.png`, then run: + +```bash +cd dashboards/ob1-canonical-landing/imgs +magick _master-square.png -resize 512x512 -strip ob1-logo.png +magick _master-square.png -resize 180x180 -strip apple-touch-icon.png +magick _master-square.png -resize 32x32 -strip favicon-32.png +magick _master-wide.png -resize 1200x -strip ob1-logo-wide.png +magick _master-wide.png -resize 1000x -background "#0f1b33" -gravity center -extent 1200x630 og.png +rm _master-square.png _master-wide.png +``` + +Requires ImageMagick 7+ (`brew install imagemagick`). The `-strip` flag removes EXIF/color-profile metadata that bloats files and leaks build-environment info. + +If the logo's color palette changes, update the four CSS custom properties in the `:root` block of `index.html`: + +```css +--accent: #e05a20; /* primary accent (OB1 orange) */ +--brand-blue-deep: #0f1b33; /* used in hero gradient + OG bg */ +--brand-blue-mid: #1a3a6e; +--brand-blue-bright: #2563b8; +``` + +### Add a new content section + +Use the existing pattern so styling, spacing, and a11y stay consistent: + +```html +
+
+

Section title

+

Lead paragraph.

+ +
+
+``` + +Alternate `
` and `
` for visual rhythm. Add a nav link in the sticky `